<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Design on Smashing Magazine — For Web Designers And Developers</title><link>https://www.smashingmagazine.com/category/design/index.xml</link><description>Recent content in Design on Smashing Magazine — For Web Designers And Developers</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 25 Dec 2025 09:32:05 +0000</lastBuildDate><item><author>Paul Boag</author><title>Giving Users A Voice Through Virtual Personas</title><link>https://www.smashingmagazine.com/2025/12/giving-users-voice-virtual-personas/</link><pubDate>Tue, 23 Dec 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/12/giving-users-voice-virtual-personas/</guid><description>Turn scattered user research into AI-powered personas that give anyone consolidated multi-perspective feedback from a single question.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/12/giving-users-voice-virtual-personas/" />
              <title>Giving Users A Voice Through Virtual Personas</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Giving Users A Voice Through Virtual Personas</h1>
                  
                    
                    <address>Paul Boag</address>
                  
                  <time datetime="2025-12-23T10:00:00&#43;00:00" class="op-published">2025-12-23T10:00:00+00:00</time>
                  <time datetime="2025-12-23T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>In my <a href="https://www.smashingmagazine.com/2025/09/functional-personas-ai-lean-practical-workflow/">previous article</a>, I explored how AI can help us create functional personas more efficiently. We looked at building personas that focus on what users are trying to accomplish rather than demographic profiles that look good on posters but rarely change design decisions.</p>

<p>But creating personas is only half the battle. The bigger challenge is getting those insights into the hands of people who need them, at the moment they need them.</p>

<p>Every day, people across your organization make decisions that affect user experience. Product teams decide which features to prioritize. Marketing teams craft campaigns. Finance teams design invoicing processes. Customer support teams write response templates. All of these decisions shape how users experience your product or service.</p>

<p>And most of them happen without any input from actual users.</p>

<h2 id="the-problem-with-how-we-share-user-research">The Problem With How We Share User Research</h2>

<p>You do the research. You create the personas. You write the reports. You give the presentations. You even make fancy infographics. And then what happens?</p>

<p>The research sits in a shared drive somewhere, slowly gathering digital dust. The personas get referenced in kickoff meetings and then forgotten. The reports get skimmed once and never opened again.</p>

<p>When a product manager is deciding whether to add a new feature, they probably do not dig through last year’s research repository. When the finance team is redesigning the invoice email, they almost certainly do not consult the user personas. They make their best guess and move on.</p>

<p>This is not a criticism of those teams. They are busy. They have deadlines. And honestly, even if they wanted to consult the research, they probably would not know where to find it or how to interpret it for their specific question.</p>

<p>The knowledge stays locked inside the heads of the UX team, who cannot possibly be present for every decision being made across the organization.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="what-if-users-could-actually-speak">What If Users Could Actually Speak?</h2>

<blockquote>What if, instead of creating static documents that people need to find and interpret, we could give stakeholders a way to consult all of your user personas at once?</blockquote>

<p>Imagine a marketing manager working on a new campaign. Instead of trying to remember what the personas said about messaging preferences, they could simply ask: <em>“I’m thinking about leading with a discount offer in this email. What would our users think?”</em></p>

<p>And the AI, drawing on all your research data and personas, could respond with a consolidated view: how each persona would likely react, where they agree, where they differ, and a set of recommendations based on their collective perspectives. One question, synthesized insight across your entire user base.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="496"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png"
			
			sizes="100vw"
			alt="Personas"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      You can question how personas will react to different scenarios based on the research available. (<a href='https://files.smashing.media/articles/giving-users-voice-virtual-personas/1-user-research-personas.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>This is not science fiction. With AI, we can build exactly this kind of system. We can take all of that scattered research (the surveys, the interviews, the support tickets, the analytics, the personas themselves) and turn it into an <strong>interactive resource</strong> that anyone can query for multi-perspective feedback.</p>

<h2 id="building-the-user-research-repository">Building the User Research Repository</h2>

<p>The foundation of this approach is a centralized repository of everything you know about your users. Think of it as a single source of truth that AI can access and draw from.</p>

<p>If you have been doing user research for any length of time, you probably have more data than you realize. It is just scattered across different tools and formats:</p>

<ul>
<li>Survey results sitting in your survey platform,</li>
<li>Interview transcripts in Google Docs,</li>
<li>Customer support tickets in your helpdesk system,</li>
<li>Analytics data in various dashboards,</li>
<li>Social media mentions and reviews,</li>
<li>Old personas from previous projects,</li>
<li>Usability test recordings and notes.</li>
</ul>

<p>The first step is gathering all of this into one place. It does not need to be perfectly organized. AI is remarkably good at making sense of messy inputs.</p>

<p>If you are starting from scratch and do not have much existing research, you can use AI deep research tools to establish a baseline.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="599"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png"
			
			sizes="100vw"
			alt="Research with perplexity"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Online deep research with a tool like perplexity can be invaluable as a starting point for user research. (<a href='https://files.smashing.media/articles/giving-users-voice-virtual-personas/2-user-research-perplexity.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>These tools can scan the web for discussions about your product category, competitor reviews, and common questions people ask. This gives you something to work with while you build out your primary research.</p>

<h2 id="creating-interactive-personas">Creating Interactive Personas</h2>

<p>Once you have your repository, the next step is creating personas that the AI can consult on behalf of stakeholders. This builds directly on <a href="https://www.smashingmagazine.com/2025/09/functional-personas-ai-lean-practical-workflow/">the functional persona approach I outlined in my previous article</a>, with one key difference: these personas become <strong>lenses</strong> through which the AI analyzes questions, not just reference documents.</p>

<p>The process works like this:</p>

<ol>
<li>Feed your research repository to an AI tool.</li>
<li>Ask it to identify distinct user segments based on goals, tasks, and friction points.</li>
<li>Have it generate detailed personas for each segment.</li>
<li>Configure the AI to consult all personas when stakeholders ask questions, providing consolidated feedback.</li>
</ol>

<p>Here is where this approach diverges significantly from traditional personas. Because the AI is the primary consumer of these persona documents, they do not need to be scannable or fit on a single page. Traditional personas are constrained by human readability: you have to distill everything down to bullet points and key quotes that someone can absorb at a glance. But AI has no such limitation.</p>

<p>This means your personas can be considerably <strong>more detailed</strong>. You can include lengthy behavioral observations, contradictory data points, and nuanced context that would never survive the editing process for a traditional persona poster. The AI can hold all of this complexity and draw on it when answering questions.</p>

<p>You can also create <strong>different lenses or perspectives within each persona</strong>, tailored to specific business functions. Your “Weekend Warrior” persona might have a marketing lens (messaging preferences, channel habits, campaign responses), a product lens (feature priorities, usability patterns, upgrade triggers), and a support lens (common questions, frustration points, resolution preferences). When a marketing manager asks a question, the AI draws on the marketing-relevant information. When a product manager asks, it pulls from the product lens. Same persona, different depth depending on who is asking.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="568"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png"
			
			sizes="100vw"
			alt="Persona Lenses"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Personas can have different lenses relevant to different functions within the business. (<a href='https://files.smashing.media/articles/giving-users-voice-virtual-personas/3-persona-lenses.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The personas should still include all the functional elements we discussed before: goals and tasks, questions and objections, pain points, touchpoints, and service gaps. But now these elements become the basis for how the AI evaluates questions from each persona’s perspective, synthesizing their views into actionable recommendations.</p>

<div class="partners__lead-place"></div>

<h2 id="implementation-options">Implementation Options</h2>

<p>You can set this up with varying levels of sophistication depending on your resources and needs.</p>

<h3 id="the-simple-approach">The Simple Approach</h3>

<p>Most AI platforms now offer project or workspace features that let you upload reference documents. In ChatGPT, these are called Projects. Claude has a similar feature. Copilot and Gemini call them Spaces or Gems.</p>

<p>To get started, create a dedicated project and upload your key research documents and personas. Then write clear instructions telling the AI to consult all personas when responding to questions. Something like:</p>

<blockquote>You are helping stakeholders understand our users. When asked questions, consult all of the user personas in this project and provide: (1) a brief summary of how each persona would likely respond, (2) an overview highlighting where they agree and where they differ, and (3) recommendations based on their collective perspectives. Draw on all the research documents to inform your analysis. If the research does not fully cover a topic, search social platforms like Reddit, Twitter, and relevant forums to see how people matching these personas discuss similar issues. If you are still unsure about something, say so honestly and suggest what additional research might help.</blockquote>

<p>This approach has some limitations. There are caps on how many files you can upload, so you might need to prioritize your most important research or consolidate your personas into a single comprehensive document.</p>

<h3 id="the-more-sophisticated-approach">The More Sophisticated Approach</h3>

<p>For larger organizations or more ongoing use, a tool like <a href="https://www.notion.com/">Notion</a> offers advantages because it can hold your entire <strong>research repository</strong> and has AI capabilities built in. You can create databases for different types of research, link them together, and then use the AI to query across everything.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="599"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png"
			
			sizes="100vw"
			alt="Notion homepage"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Notion is a powerful tool for user research with built-in AI functionality that can refer to all your personas as well as your entire research repository. (<a href='https://files.smashing.media/articles/giving-users-voice-virtual-personas/4-notion-user-research.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The benefit here is that the AI has access to much <strong>more context</strong>. When a stakeholder asks a question, it can draw on surveys, support tickets, interview transcripts, and analytics data all at once. This makes for richer, more nuanced responses.</p>

<h2 id="what-this-does-not-replace">What This Does Not Replace</h2>

<p>I should be clear about the limitations.</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aVirtual%20personas%20are%20not%20a%20substitute%20for%20talking%20to%20real%20users.%20They%20are%20a%20way%20to%20make%20existing%20research%20more%20accessible%20and%20actionable.%0a&url=https://smashingmagazine.com%2f2025%2f12%2fgiving-users-voice-virtual-personas%2f">
      
Virtual personas are not a substitute for talking to real users. They are a way to make existing research more accessible and actionable.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<p>There are several scenarios where you still need primary research:</p>

<ul>
<li>When launching something genuinely new that your existing research does not cover;</li>
<li>When you need to validate specific designs or prototypes;</li>
<li>When your repository data is getting stale;</li>
<li>When stakeholders need to hear directly from real humans to build empathy.</li>
</ul>

<p>In fact, you can configure the AI to recognize these situations. When someone asks a question that goes beyond what the research can answer, the AI can respond with something like: <em>“I do not have enough information to answer that confidently. This might be a good question for a quick user interview or survey.”</em></p>

<p>And when you do conduct new research, that data feeds back into the repository. The personas evolve over time as your understanding deepens. This is much better than the traditional approach, where personas get created once and then slowly drift out of date.</p>

<div class="partners__lead-place"></div>

<h2 id="the-organizational-shift">The Organizational Shift</h2>

<p>If this approach catches on in your organization, something interesting happens.</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aThe%20UX%20team%e2%80%99s%20role%20shifts%20from%20being%20the%20gatekeepers%20of%20user%20knowledge%20to%20being%20the%20curators%20and%20maintainers%20of%20the%20repository.%0a&url=https://smashingmagazine.com%2f2025%2f12%2fgiving-users-voice-virtual-personas%2f">
      
The UX team’s role shifts from being the gatekeepers of user knowledge to being the curators and maintainers of the repository.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<p>Instead of spending time creating reports that may or may not get read, you spend time ensuring the repository stays current and that the AI is configured to give helpful responses.</p>

<p>Research communication changes from push (presentations, reports, emails) to pull (stakeholders asking questions when they need answers). <strong>User-centered thinking</strong> becomes distributed across the organization rather than concentrated in one team.</p>

<p>This does not make UX researchers less valuable. If anything, it makes them more valuable because their work now has a wider reach and greater impact. But it does change the nature of the work.</p>

<h2 id="getting-started">Getting Started</h2>

<p>If you want to try this approach, start small. If you need a primer on functional personas before diving in, I have written a <a href="https://boagworld.com/usability/personas/">detailed guide to creating them</a>. Pick one project or team and set up a simple implementation using ChatGPT Projects or a similar tool. Gather whatever research you have (even if it feels incomplete), create one or two personas, and see how stakeholders respond.</p>

<p>Pay attention to what questions they ask. These will tell you where your research has gaps and what additional data would be most valuable.</p>

<p>As you refine the approach, you can expand to more teams and more sophisticated tooling. But the core principle stays the same: <strong>take all that scattered user knowledge and give it a voice that anyone in your organization can hear.</strong></p>

<p>In my previous article, I argued that we should move from demographic personas to functional personas that focus on what users are trying to do. Now I am suggesting we take the next step: from static personas to interactive ones that can actually participate in the conversations where decisions get made.</p>

<p>Because every day, across your organization, people are making decisions that affect your users. And your users deserve a seat at the table, even if it is a virtual one.</p>

<h3 id="further-reading-on-smashingmag">Further Reading On SmashingMag</h3>

<ul>
<li>“<a href="https://www.smashingmagazine.com/2014/08/a-closer-look-at-personas-part-1/">A Closer Look At Personas: What They Are And How They Work | 1</a>”, Shlomo Goltz</li>
<li>“<a href="https://www.smashingmagazine.com/2018/04/design-process-data-based-personas/">How To Improve Your Design Process With Data-Based Personas</a>”, Tim Noetzel</li>
<li>“<a href="https://www.smashingmagazine.com/2025/10/how-make-ux-research-hard-to-ignore/">How To Make Your UX Research Hard To Ignore</a>”, Vitaly Friedman</li>
<li>“<a href="https://www.smashingmagazine.com/2023/01/build-strong-customer-relationships-user-research/">How To Build Strong Customer Relationships For User Research</a>”, Renaissance Rachel</li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Vitaly Friedman</author><title>How To Measure The Impact Of Features</title><link>https://www.smashingmagazine.com/2025/12/how-measure-impact-features-tars/</link><pubDate>Fri, 19 Dec 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/12/how-measure-impact-features-tars/</guid><description>Meet TARS — a simple, repeatable, and meaningful UX metric designed specifically to track the performance of product features. Upcoming part of the &lt;a href="https://measure-ux.com/">Measure UX &amp;amp; Design Impact&lt;/a> (use the code 🎟 &lt;code>IMPACT&lt;/code> to save 20% off today).</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/12/how-measure-impact-features-tars/" />
              <title>How To Measure The Impact Of Features</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>How To Measure The Impact Of Features</h1>
                  
                    
                    <address>Vitaly Friedman</address>
                  
                  <time datetime="2025-12-19T10:00:00&#43;00:00" class="op-published">2025-12-19T10:00:00+00:00</time>
                  <time datetime="2025-12-19T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>So we design and ship a <strong>shiny new feature</strong>. How do we know if it’s working? How do we measure and track its impact? There is <a href="https://measuringu.com/an-overview-of-70-ux-metrics/">no shortage in UX metrics</a>, but what if we wanted to establish a <strong>simple, repeatable</strong>, meaningful UX metric &mdash; specifically for our features? Well, let’s see how to do just that.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="975"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg"
			
			sizes="100vw"
			alt="Adrian Raudaschl&#39;s framework for measuring feature impact."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      With <a href='https://uxdesign.cc/tars-a-product-metric-game-changer-c523f260306a?sk=v2%2F2a9d7d1e-bae9-4875-9063-4b6a10ae110c'>TARS</a>, we can assess how effective features are and how well they are performing.(<a href='https://files.smashing.media/articles/how-measure-impact-features-tars/1-impact-features-tars.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I first heard about the <strong>TARS framework</strong> from Adrian H. Raudschl’s wonderful article on “<a href="https://uxdesign.cc/tars-a-product-metric-game-changer-c523f260306a?sk=v2%2F2a9d7d1e-bae9-4875-9063-4b6a10ae110c">How To Measure Impact of Features</a>”. Here, Adrian highlighted how his team tracks and decides which features to focus on &mdash; and then maps them against each other in a <strong>2×2 quadrants matrix</strong>.</p>

<p>It turned out to be a very useful framework to <strong>visualize</strong> the impact of UX work through the lens of business metrics.</p>

<p>Let’s see how it works.</p>

<h2 id="1-target-audience">1. Target Audience (%)</h2>

<p>We start by quantifying the <strong>target audience</strong> by exploring what percentage of a product’s users have the specific problem that a feature aims to solve. We can study existing or similar features that try to solve similar problems, and how many users engage with them.</p>

<p>Target audience <strong>isn’t the same</strong> as feature usage though. As Adrian noted, if we know that an existing Export Button feature is used by 5% of all users, it doesn’t mean that the target audience is 5%. <strong>More users</strong> might have the problem that the export feature is trying to solve, but they can’t find it.</p>

<blockquote>Question we ask: “What percentage of all our product’s users have that specific problem that a new feature aims to solve?”</blockquote>

<h2 id="2-a-adoption">2. A = Adoption (%)</h2>

<p>Next, we measure how well we are <strong>“acquiring”</strong> our target audience. For that, we track how many users actually engage <em>successfully</em> with that feature over a specific period of time.</p>

<p>We <strong>don’t focus on CTRs or session duration</strong> there, but rather if users <em>meaningfully</em> engage with it. For example, if anything signals that they found it valuable, such as sharing the export URL, the number of exported files, or the usage of filters and settings.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="395"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg"
			
			sizes="100vw"
			alt="The TARS Framework Step"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Adoption rates: from low adoption (<20%) to high adoption (>60%). Illustration by <a href='https://uxdesign.cc/tars-a-product-metric-game-changer-c523f260306a?sk=v2%2F2a9d7d1e-bae9-4875-9063-4b6a10ae110c'>Adrian Raudaschl</a>. (<a href='https://files.smashing.media/articles/how-measure-impact-features-tars/2-impact-features-tars.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>High <strong>feature adoption</strong> (&gt;60%) suggests that the problem was impactful. Low adoption (&lt;20%) might imply that the problem has simple workarounds that people have relied upon. Changing habits takes time, too, and so low adoption in the beginning is expected.</p>

<p>Sometimes, low feature adoption has nothing to do with the feature itself, but rather <strong>where it sits in the UI</strong>. Users might never discover it if it’s hidden or if it has a confusing label. It must be obvious enough for people to stumble upon it.</p>

<p>Low adoption doesn’t always equal failure. If a problem only affects 10% of users, hitting 50–75% adoption within that specific niche means the feature is a <strong>success</strong>.</p>

<blockquote>Question we ask: “What percentage of active target users actually use the feature to solve that problem?”</blockquote>

<h2 id="3-retention">3. Retention (%)</h2>

<p>Next, we study whether a feature is actually used repeatedly. We measure the frequency of use, or specifically, how many users who engaged with the feature actually keep using it over time. Typically, it’s a strong signal for <strong>meaningful impact</strong>.</p>

<p>If a feature has &gt;50% retention rate (avg.), we can be quite confident that it has a <strong>high strategic importance</strong>. A 25–35% retention rate signals medium strategic significance, and retention of 10–20% is then low strategic importance.</p>

<blockquote>Question we ask: “Of all the users who meaningfully adopted a feature, how many came back to use it again?”</blockquote>

<h2 id="4-satisfaction-score-ces">4. Satisfaction Score (CES)</h2>

<p>Finally, we measure the <strong>level of satisfaction</strong> that users have with that feature that we’ve shipped. We don’t ask everyone &mdash; we ask only “retained” users. It helps us spot hidden troubles that might not be reflected in the retention score.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="395"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg"
			
			sizes="100vw"
			alt="Customer Satisfaction Score, measured with a survey"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      We ask users how easy it was to solve a problem after they used a feature. Illustration by <a href='https://uxdesign.cc/tars-a-product-metric-game-changer-c523f260306a?sk=v2%2F2a9d7d1e-bae9-4875-9063-4b6a10ae110c'>Adrian Raudaschl</a>. (<a href='https://files.smashing.media/articles/how-measure-impact-features-tars/3-impact-features-tars.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Once users actually used a feature multiple times, we ask them <strong>how easy it was to solve</strong> a problem after they used that feature &mdash; between “much more difficult” and “much easier than expected”. We know how we want to score.</p>

<h2 id="using-tars-for-feature-strategy">Using TARS For Feature Strategy</h2>

<p>Once we start measuring with TARS, we can calculate an <strong>S÷T score</strong> &mdash; the percentage of Satisfied Users ÷ Target Users. It gives us a sense of how well a feature is performing for our intended target audience. Once we do that for every feature, we can map all features across 4 quadrants in a <strong>2×2 matrix</strong>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="400"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg"
			
			sizes="100vw"
			alt="Feature retention curves"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Evaluating features on a 2×2 matrix based on S/T score Illustration by <a href='https://uxdesign.cc/tars-a-product-metric-game-changer-c523f260306a?sk=v2%2F2a9d7d1e-bae9-4875-9063-4b6a10ae110c'>Adrian Raudaschl</a>. (<a href='https://files.smashing.media/articles/how-measure-impact-features-tars/4-impact-features-tars.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p><strong>Overperforming features</strong> are worth paying attention to: they have low retention but high satisfaction. It might simply be features that users don’t have to use frequently, but when they do, it’s extremely effective.</p>

<p><strong>Liability features</strong> have high retention but low satisfaction, so perhaps we need to work on them to improve them. And then we can also identify <strong>core features</strong> and project features &mdash; and have a conversation with designers, PMs, and engineers on what we should work on next.</p>

<div class="partners__lead-place"></div>

<h2 id="conversion-rate-is-not-a-ux-metric">Conversion Rate Is Not a UX Metric</h2>

<p>TARS doesn’t cover conversion rate, and for a good reason. As <a href="https://www.linkedin.com/posts/fabian-lenz-digital-experience-leadership_conversion-rate-is-not-a-ux-metric-yes-activity-7394261839506739200-78G9">Fabian Lenz noted</a>, conversion is often considered to be the <strong>ultimate indicator of success</strong> &mdash; yet in practice it’s always very difficult to present a clear connection between smaller design initiatives and big conversion goals.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="274"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png"
			
			sizes="100vw"
			alt="Chart comparing Leading vs Lagging Measures for UX metrics"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Leading vs. Lagging Measures by <a href='https://measuringu.com/leading-vs-lagging/'>Jeff Sauro and James R. Lewis</a>. (But please do avoid NPS at all costs). (<a href='https://files.smashing.media/articles/how-measure-impact-features-tars/5-impact-features-tars.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The truth is that almost everybody on the team is working towards better conversion. An uptick might be connected to <strong>many different initiatives</strong> &mdash; from sales and marketing to web performance boost to seasonal effects to UX initiatives.</p>

<p>UX can, of course, improve conversion, but it’s not really a UX metric. Often, people simply <strong>can’t choose the product</strong> they are using. And often a desired business outcome comes out of necessity and struggle, rather than trust and appreciation.</p>

<h3 id="high-conversion-despite-bad-ux">High Conversion Despite Bad UX</h3>

<p>As Fabian <a href="https://www.linkedin.com/posts/fabian-lenz-digital-experience-leadership_conversion-rate-is-not-a-ux-metric-yes-activity-7394261839506739200-78G9/">writes</a>, <strong>high conversion rate</strong> can happen despite poor UX, because:</p>

<ul>
<li><strong>Strong brand power</strong> pulls people in,</li>
<li>Aggressive but effective <strong>urgency tactics</strong>,</li>
<li>Prices are extremely attractive,</li>
<li>Marketing performs brilliantly,</li>
<li>Historical customer loyalty,</li>
<li>Users simply have no alternative.</li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="509"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg"
			
			sizes="100vw"
			alt="UX Scorecard and design metrics overview"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      A practical overview of design metrics and UX scorecards: <a href='https://uxplanet.org/measuring-ux-your-first-step-towards-objective-evaluation-a408b312777b'>Measuring UX: Your First Step Towards Objective Evaluation</a> by Roman Videnov. (<a href='https://files.smashing.media/articles/how-measure-impact-features-tars/6-impact-features-tars.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<h3 id="low-conversion-despite-great-ux">Low Conversion Despite Great UX</h3>

<p>At the same time, a low conversion rate can occur despite great UX, because:</p>

<ul>
<li><strong>Offers aren’t relevant</strong> to the audience,</li>
<li><strong>Users don’t trust the brand</strong>,</li>
<li>Poor business model or high risk of failure,</li>
<li>Marketing doesn’t reach the right audience,</li>
<li>External factors (price, timing, competition).</li>
</ul>

<p>An improved conversion is the <strong>positive outcome of UX initiatives</strong>. But good UX work typically improves task completion, reduces time on task, minimizes errors, and avoids decision paralysis. And there are plenty of <a href="https://www.linkedin.com/posts/vitalyfriedman_how-to-measure-ux-httpslnkdine5uedtzy-activity-7332664809382952960-HERA">actionable design metrics we could use</a> to track UX and drive sustainable success.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p><strong>Product metrics</strong> alone don’t always provide an accurate view of how well a product performs. Sales might perform well, but users might be extremely inefficient and frustrated. Yet the churn is low because users can’t choose the tool they are using.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg"
			
			sizes="100vw"
			alt="Chart comparing Leading vs Lagging Measures for UX metrics"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <a href='https://www.linkedin.com/posts/vitalyfriedman_ux-design-activity-7140641630507687936-YTI7'>Design KPIs and UX Metrics</a>, a quick overview by yours truly. Numbers are, of course, placeholders. (<a href='https://files.smashing.media/articles/how-measure-impact-features-tars/7-impact-features-tars.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>We need UX metrics to understand and improve user experience. What I love most about TARS is that it’s a neat way to connect customers’ usage and <strong>customers’ experience with relevant product metrics</strong>. Personally, I would extend TARS with <a href="https://www.linkedin.com/posts/vitalyfriedman_ux-design-activity-7140641630507687936-YTI7">UX-focused metrics and KPIs</a> as well &mdash; depending on the needs of the project.</p>

<p>Huge thanks to <a href="https://www.linkedin.com/in/adrian-raudaschl/">Adrian H. Raudaschl</a> for putting it together. And if you are interested in metrics, I highly recommend you follow him for practical and useful guides all around just that!</p>

<h2 id="meet-how-to-measure-ux-and-design-impact">Meet “How To Measure UX And Design Impact”</h2>

<p>You can find more details on <strong>UX Strategy</strong> in 🪴&nbsp;<a href="https://measure-ux.com/"><strong>Measure UX &amp; Design Impact</strong></a> (8h), a practical guide for designers and UX leads to measure and show your UX impact on business. Use the code 🎟 <code>IMPACT</code> to save 20% off today. <a href="https://measure-ux.com/">Jump to the details</a>.</p>

<figure style="margin-bottom:0;padding-bottom:0" class="article__image">
    <a href="https://measure-ux.com/" title="How To Measure UX and Design Impact, with Vitaly Friedman">
    <img width="900" height="466" style="border-radius: 11px" src="https://files.smashing.media/articles/ux-metrics-video-course-release/measure-ux-and-design-impact-course.png" alt="How to Measure UX and Design Impact, with Vitaly Friedman.">
    </a>
</figure>

<div class="book-cta__inverted"><div class="book-cta" data-handler="ContentTabs" data-mq="(max-width: 480px)"><nav class="content-tabs content-tabs--books"><ul><li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">
Video + UX Training</button></a></li><li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">Video only</button></a></li></ul></nav><div class="book-cta__col book-cta__hardcover content-tab--content"><h3 class="book-cta__title"><span>Video + UX Training</span></h3><span class="book-cta__price"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>495<span class="sup">.00</span></span></span> <span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>799<span class="sup">.00</span></span></span></span></span>
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3951439" class="btn btn--full btn--medium btn--text-shadow">
Get Video + UX Training<div></div></a><p class="book-cta__desc">25 video lessons (8h) + <a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">Live UX Training</a>.<br>100 days money-back-guarantee.</p></div><div class="book-cta__col book-cta__ebook content-tab--content"><h3 class="book-cta__title"><span>Video only</span></h3><div data-audience="anonymous free supporter" data-remove="true"><span class="book-cta__price" data-handler="PriceTag"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>250<span class="sup">.00</span></span></span><span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>395<span class="sup">.00</span></span></span></span></div>
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3950630" class="btn btn--full btn--medium btn--text-shadow">
Get the video course<div></div></a><p class="book-cta__desc" data-audience="anonymous free supporter" data-remove="true">25 video lessons (8h). Updated yearly.<br>Also available as a <a href="https://smart-interface-design-patterns.thinkific.com/enroll/3570306?price_id=4503439">UX Bundle with 3 video courses.</a></p></div><span></span></div></div>

<h2 id="useful-resources">Useful Resources</h2>

<ul>
<li>“<a href="https://measure-ux.com">How To Measure UX and Design Impact</a>”, by yours truly</li>
<li>“<a href="https://thecdo.school/books">Business Thinking For Designers</a>”, by Ryan Rumsey</li>
<li>“<a href="https://www.linkedin.com/feed/update/urn:li:activity:7338462034763661312/">ROI of Design Project</a></li>
<li>“<a href="https://articles.centercentre.com/how-the-right-ux-metrics-show-game-changing-value/">How the Right UX Metrics Show Game-Changing Value</a>”, by Jared Spool</li>
<li>“<a href="https://www.linkedin.com/posts/vitalyfriedman_ux-design-research-activity-7164173642887606274-rEqq">Research Sample Size Calculators</a>”</li>
</ul>

<h3 id="further-reading">Further Reading</h3>

<ul>
<li>“<a href="https://www.smashingmagazine.com/2025/11/designing-for-stress-emergency/">Designing For Stress And Emergency</a>”, Vitaly Friedman</li>
<li>“<a href="https://www.smashingmagazine.com/2025/10/ai-ux-achieve-more-with-less/">AI In UX: Achieve More With Less</a>”, Paul Boag</li>
<li>“<a href="https://www.smashingmagazine.com/2025/11/accessibility-problem-authentication-methods-captcha/">The Accessibility Problem With Authentication Methods Like CAPTCHA</a>”, Eleanor Hecks</li>
<li>“<a href="https://www.smashingmagazine.com/2025/09/from-prompt-to-partner-designing-custom-ai-assistant/">From Prompt To Partner: Designing Your Custom AI Assistant</a>”, Lyndon Cerejo</li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Andy Clarke</author><title>Smashing Animations Part 7: Recreating Toon Text With CSS And SVG</title><link>https://www.smashingmagazine.com/2025/12/smashing-animations-part-7-recreating-toon-text-css-svg/</link><pubDate>Wed, 17 Dec 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/12/smashing-animations-part-7-recreating-toon-text-css-svg/</guid><description>In this article, pioneering author and web designer &lt;a href="https://stuffandnonsense.co.uk">Andy Clarke&lt;/a> shows his techniques for creating &lt;a href="https://stuffandnonsense.co.uk/toon-text/index.html">Toon Text titles&lt;/a> using modern CSS and SVG.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/12/smashing-animations-part-7-recreating-toon-text-css-svg/" />
              <title>Smashing Animations Part 7: Recreating Toon Text With CSS And SVG</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Smashing Animations Part 7: Recreating Toon Text With CSS And SVG</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-12-17T10:00:00&#43;00:00" class="op-published">2025-12-17T10:00:00+00:00</time>
                  <time datetime="2025-12-17T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>After finishing a project that required me to learn everything I could about CSS and SVG animations, I started writing this series about Smashing Animations and “<a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-1-classic-cartoons-inspire-css/">How Classic Cartoons Inspire Modern CSS</a>.” To round off this year, I want to show you how to use modern CSS to create that element that makes Toon Titles so impactful: their typography.</p>

<h2 id="title-artwork-design">Title Artwork Design</h2>

<p>In the silent era of the 1920s and early ’30s, the typography of a film’s title card created a mood, set the scene, and reminded an audience of the type of film they’d paid to see.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="156"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png"
			
			sizes="100vw"
			alt="Typographic title cards from the early years of cinema"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Typographic title cards from the early years of cinema. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/1-typographic-title-cards.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Cartoon title cards were also branding, mood, and scene-setting, all rolled into one. In the early years, when major studio budgets were bigger, these title cards were often illustrative and painterly.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="300"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png"
			
			sizes="100vw"
			alt="Top: William Hanna and Joseph Barbera’s 1940s Tom &amp; Jerry title cards. Bottom: Colour versions released in 1957. © Warner Bros. Entertainment Inc."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Top: William Hanna and Joseph Barbera’s 1940s Tom & Jerry title cards. Bottom: Colour versions released in 1957. © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/2-tom-jerry-title-cards.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But when television boomed during the 1950s, budgets dropped, and cards designed by artists like Lawrence “Art” Goble adopted a new visual language, becoming more graphic, stylised, and less intricate.</p>

<p><strong>Note:</strong> <em>Lawrence “Art” Goble is one of the often overlooked heroes of mid-century American animation. He primarily worked for Hanna-Barbera during its most influential years of the 1950s and 1960s.</em></p>

<p>Goble wasn’t a character animator. His role was to create atmosphere, so he designed environments for <em>The Flintstones</em>, <em>Huckleberry Hound</em>, <em>Quick Draw McGraw</em>, and <em>Yogi Bear</em>, as well as the opening title cards that set the tone. His title cards, featuring paintings with a logo overlaid, helped define the iconic look of Hanna-Barbera.</p>

<p>Goble’s artwork for characters such as Quick Draw McGraw and Yogi Bear was effective on smaller TV screens. Rather than reproducing a still from the cartoon, he focused on presenting a single, strong idea &mdash; often in silhouette &mdash; that captured its essence. In “The Buzzin’ Bear,” Yogi buzzes by in a helicopter. He bounces away, pic-a-nic basket in hand, in “Bear on a Picnic,” and for his “Prize Fight Fright,” Yogi boxes the title text.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="300"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png"
			
			sizes="100vw"
			alt="Title cards for Hanna-Barbera’s Yogi Bear."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Title cards for Hanna-Barbera’s Yogi Bear. © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/3-title-cards-yogi-bear.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>With little or no motion to rely on, Goble’s single frames had to create a mood, set the scene, and describe a story. They did this using flat colours, graphic shapes, and typography that was frequently integrated into the artwork.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="225"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png"
			
			sizes="100vw"
			alt="Title cards for Hanna-Barbera’s Quick Draw McGraw."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Title cards for Hanna-Barbera’s Quick Draw McGraw. © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/4-title-cards-quick-draw-mcgraw.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>As designers who work on the web, toon titles can teach us plenty about how to convey a brand’s personality, make a first impression, and set expectations for someone’s experience using a product or website. We can learn from the artists’ techniques to create effective banners, landing-page headers, and even good ol’ fashioned splash screens.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="toon-title-typography">Toon Title Typography</h2>

<p>Cartoon title cards show how merging type with imagery delivers the punch a header or hero needs. With a handful of <code>text-shadow</code>, <code>text-stroke</code>, and <code>transform</code> tricks, modern CSS lets you tap into that same energy.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="455"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png"
			
			sizes="100vw"
			alt="Title cards for Hanna-Barbera’s Augie Doggie."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Title cards for Hanna-Barbera’s Augie Doggie. © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/5-title-cards-augie-doggie.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="the-toon-text-title-generator">The Toon Text Title Generator</h2>

<p>Partway through writing this article, I realised it would be useful to have a tool for generating text styled like the cartoon titles I love so much. <a href="https://stuffandnonsense.co.uk/toon-text/tool.html">So I made one.</a></p>

<p>My Toon Text Title Generator lets you experiment with colours, strokes, and multiple text shadows. You can adjust paint order, apply letter spacing, preview your text in a selection of sample fonts, and then copy the generated CSS straight to your clipboard to use in a project.</p>

<h2 id="toon-title-css">Toon Title CSS</h2>

<p>You can simply copy-paste the CSS that the Toon Text Title Generator provides you. But let’s look closer at what it does.</p>

<h3 id="text-shadow">Text shadow</h3>

<p>Look at the type in this title from Augie Doggie’s episode “Yuk-Yuk Duck,” with its pale yellow letters and dark, hard, offset shadow that lifts it off the background and creates the illusion of depth.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/6-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>You probably already know that <code>text-shadow</code> accepts four values: (1) horizontal and (2) vertical offsets, (3) blur, and (4) a colour which can be solid or semi-transparent. Those offset values can be positive or negative, so I can replicate “Yuk-Yuk Duck” using a hard shadow pulled down and to the right:</p>

<pre><code class="language-css">color: &#35;f7f76d;
text-shadow: 5px 5px 0 &#35;1e1904;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/7-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>On the other hand, this “Pint Giant” title has a different feel with its negative semi-soft shadow:</p>

<pre><code class="language-css">color: &#35;c2a872;
text-shadow:
  -7px 5px 0 &#35;b100e,
  0 -5px 10px &#35;546c6f;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/8-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>To add extra depth and create more interesting effects, I can layer multiple shadows. For “Let’s Duck Out,” I combine four shadows: the first a solid shadow with a negative horizontal offset to lift the text off the background, followed by progressively softer shadows to create a blur around it:</p>

<pre><code class="language-css">color: &#35;6F4D80;
text-shadow:
  -5px 5px 0 &#35;260e1e, /&#42; Shadow 1 &#42;/
  0 0 15px &#35;e9ce96,   /&#42; Shadow 2 &#42;/
  0 0 30px &#35;e9ce96,   /&#42; Shadow 3 &#42;/
  0 0 30px &#35;e9ce96;   /&#42; Shadow 4 &#42;/
</code></pre>

<p>These shadows show that using <code>text-shadow</code> isn’t just about creating lighting effects, as they can also be decorative and add personality.</p>

<h3 id="text-stroke">Text Stroke</h3>

<p>Many cartoon title cards feature letters with a bold outline that makes them stand out from the background. I can recreate this effect using <code>text-stroke</code>. For a long time, this property was only available via a <code>-webkit-</code> prefix, but that also means it’s now supported across modern browsers.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/9-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p><code>text-stroke</code> is a shorthand for two properties. The first, <code>text-stroke-width</code>, draws a contour around individual letters, while the second, <code>text-stroke-color</code>, controls its colour. For “Whatever Goes Pup,” I added a <code>4px</code> blue stroke to the yellow text:</p>

<pre><code class="language-css">color: &#35;eff0cd;
-webkit-text-stroke: 4px &#35;7890b5;
text-stroke: 4px &#35;7890b5;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/10-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Strokes can be especially useful when they’re combined with shadows, so for “Growing, Growing, Gone,” I added a thin <code>3px</code> stroke to a barely blurred <code>1px</code> shadow to create this three-dimensional text effect:</p>

<pre><code class="language-css">color: &#35;fbb999;
text-shadow: 3px 5px 1px &#35;5160b1;
-webkit-text-stroke: 3px &#35;984336;
text-stroke: 3px &#35;984336;
</code></pre>

<h3 id="paint-order">Paint Order</h3>

<p>Using <code>text-stroke</code> doesn’t always produce the expected result, especially with thinner letters and thicker strokes, because by default the browser draws a stroke over the fill. Sadly, CSS still does not permit me to adjust stroke placement as I often do in Sketch. However, the <code>paint-order</code> property has values that allow me to place the stroke behind, rather than in front of, the fill.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png"
			
			sizes="100vw"
			alt="Left: paint-order: stroke; Right: paint-order: fill."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Left: <code>paint-order: stroke</code>. Right: <code>paint-order: fill</code>. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/11-paint-order.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p><code>paint-order: stroke</code> paints the stroke first, then the fill, whereas <code>paint-order: fill</code> does the opposite:</p>

<pre><code class="language-css">color: &#35;fbb999;
paint-order: fill;
text-shadow: 3px 5px 1px &#35;5160b1;
text-stroke-color:&#35;984336;
text-stroke-width: 3px;
</code></pre>

<p>An effective stroke keeps letters readable, adds weight, and &mdash; when combined with shadows and paint order &mdash; gives flat text real presence.</p>

<div class="partners__lead-place"></div>

<h2 id="backgrounds-inside-text">Backgrounds Inside Text</h2>

<p>Many cartoon title cards go beyond flat colour by adding texture, gradients, or illustrated detail to the lettering. Sometimes that’s a texture, other times it might be a gradient with a subtle tonal shift. On the web, I can recreate this effect by using a background image or gradient behind the text, and then clipping it to the shape of the letters. This relies on two properties working together: <code>background-clip: text</code> and <code>text-fill-color: transparent</code>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/12-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>First, I apply a background behind the text. This can be a bitmap or vector image or a CSS gradient. For this example from the Quick Draw McGraw episode “Baba Bait,” the title text includes a subtle top–bottom gradient from dark to light:</p>

<pre><code class="language-css">background: linear-gradient(0deg, &#35;667b6a, &#35;1d271a);
</code></pre>

<p>Next, I clip that background to the glyphs and make the text transparent so the background shows through:</p>

<pre><code class="language-css">-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
</code></pre>

<p>With just those two lines, the background is no longer painted behind the text; instead, it’s painted within it. This technique works especially well when combined with strokes and shadows. A clipped gradient provides the lettering with colour and texture, a stroke keeps its edges sharp, and a shadow elevates it from the background. Together, they recreate the layered look of hand-painted title cards using nothing more than a little CSS. As always, test clipped text carefully, as browser quirks can sometimes affect shadows and rendering.</p>

<h3 id="splitting-text-into-individual-characters">Splitting Text Into Individual Characters</h3>

<p>Sometimes I don’t want to style a whole word or heading. I want to style individual letters &mdash; to nudge a character into place, give one glyph extra weight, or animate a few letters independently.</p>

<p>In plain HTML and CSS, there’s only one reliable way to do that: wrap each character in its own <code>span</code> element. I could do that manually, but that would be fragile, hard to maintain, and would quickly fall apart when copy changes. Instead, when I need per-letter control, I use a text-splitting library like <a href="https://www.spltjs.com">splt.js</a> (although other solutions are available). This takes a text node and automatically wraps words or characters, giving me extra hooks to animate and style without messing up my markup.</p>

<p>It’s an approach that keeps my HTML readable and semantic, while giving me the fine-grained control I need to recreate the uneven, characterful typography you see in classic cartoon title cards. However, this approach comes with accessibility caveats, as most screen readers read text nodes in order. So this:</p>

<pre><code class="language-html">&lt;h2&gt;Hum Sweet Hum&lt;/h2&gt;
</code></pre>

<p>…reads as you’d expect:</p>

<blockquote>Hum Sweet Hum</blockquote>

<p>But this:</p>

<pre><code class="language-html">&lt;h2&gt;
&lt;span&gt;H&lt;/span&gt;
&lt;span&gt;u&lt;/span&gt;
&lt;span&gt;m&lt;/span&gt;
&lt;!-- etc. --&gt;
&lt;/h2&gt;
</code></pre>

<p>…can be interpreted differently depending on the browser and screen reader. Some will concatenate the letters and read the words correctly. Others may pause between letters, which in a worst-case scenario might sound like:</p>

<blockquote>“H…” “U…” “M…”</blockquote>

<p>Sadly, some splitting solutions don’t deliver an always accessible result, so I’ve written my own text splitter, <a href="https://stuffandnonsense.co.uk/toon-text/splinter.html#section-install">splinter.js</a>, which is currently in beta.</p>

<h3 id="transforming-individual-letters">Transforming Individual Letters</h3>

<p>To activate my Toon Text Splitter, I add a <code>data-</code> attribute to the element I want to split:</p>

<pre><code class="language-html">&lt;h2 data-split="toon"&gt;Hum Sweet Hum&lt;/h2&gt;
</code></pre>

<p>First, my script separates each word into individual letters and wraps them in a <code>span</code> element with class and ARIA attributes applied:</p>

<pre><code class="language-html">&lt;span class="toon-char" aria-hidden="true"&gt;H&lt;/span&gt;
&lt;span class="toon-char" aria-hidden="true"&gt;u&lt;/span&gt;
&lt;span class="toon-char" aria-hidden="true"&gt;m&lt;/span&gt;
</code></pre>

<p>The script then takes the initial content of the split element and adds it as an aria attribute to help maintain accessibility:</p>

<div class="break-out">
<pre><code class="language-html">&lt;h2 data-split="toon" aria-label="Hum Sweet Hum"&gt;
  &lt;span class="toon-char" aria-hidden="true"&gt;H&lt;/span&gt;
  &lt;span class="toon-char" aria-hidden="true"&gt;u&lt;/span&gt;
  &lt;span class="toon-char" aria-hidden="true"&gt;m&lt;/span&gt;
&lt;/h2&gt;
</code></pre>
</div>

<p>With those class attributes applied, I can then style individual characters as I choose.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/13-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>For example, for “Hum Sweet Hum,” I want to replicate how its letters shift away from the baseline. After using my Toon Text Splitter, I applied four different <code>translate</code> values using several <code>:nth-child</code> selectors to create a semi-random look:</p>

<pre><code class="language-css">/&#42; 4th, 8th, 12th... &#42;/
.toon-char:nth-child(4n) { translate: 0 -8px; }
/&#42; 1st, 5th, 9th... &#42;/
.toon-char:nth-child(4n+1) { translate: 0 -4px; }
/&#42; 2nd, 6th, 10th... &#42;/
.toon-char:nth-child(4n+2) { translate: 0 4px; }
/&#42; 3rd, 7th, 11th... &#42;/
.toon-char:nth-child(4n+3) { translate: 0 8px; }
</code></pre>

<p>But <code>translate</code> is only one property I can use to <code>transform</code> my toon text.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/14-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I could also rotate those individual characters for an even more chaotic look:</p>

<pre><code class="language-css">/&#42; 4th, 8th, 12th... &#42;/
.toon-line .toon-char:nth-child(4n) { rotate: -4deg; }
/&#42; 1st, 5th, 9th... &#42;/
.toon-char:nth-child(4n+1) { rotate: -8deg; }
/&#42; 2nd, 6th, 10th... &#42;/
.toon-char:nth-child(4n+2) { rotate: 4deg; }
/&#42; 3rd, 7th, 11th... &#42;/
.toon-char:nth-child(4n+3) { rotate: 8deg; }
</code></pre>

<p>But <code>translate</code> is only one property I can use to <code>transform</code> my toon text. I could also <code>rotate</code> those individual characters for an even more chaotic look:</p>

<pre><code class="language-css">/&#42; 4th, 8th, 12th... &#42;/
.toon-line .toon-char:nth-child(4n) {
rotate: -4deg; }

/&#42; 1st, 5th, 9th... &#42;/
.toon-char:nth-child(4n+1) {
rotate: -8deg; }

/&#42; 2nd, 6th, 10th... &#42;/
.toon-char:nth-child(4n+2) {
rotate: 4deg; }

/&#42; 3rd, 7th, 11th... &#42;/
.toon-char:nth-child(4n+3) {
rotate: 8deg; }
</code></pre>

<p>And, of course, I could add animations to jiggle those characters and bring my toon text style titles to life. First, I created a keyframe animation that rotates the characters:</p>

<div class="break-out">
<pre><code class="language-css">@keyframes jiggle {
0%, 100% { transform: rotate(var(--base-rotate, 0deg)); }
25% { transform: rotate(calc(var(--base-rotate, 0deg) + 3deg)); }
50% { transform: rotate(calc(var(--base-rotate, 0deg) - 2deg)); }
75% { transform: rotate(calc(var(--base-rotate, 0deg) + 1deg)); }
}
</code></pre>
</div>

<p>Before applying it to the <code>span</code> elements created by my Toon Text Splitter:</p>

<pre><code class="language-css">.toon-char {
animation: jiggle 3s infinite ease-in-out;
transform-origin: center bottom; }
</code></pre> 

<p>And finally, setting the rotation amount and a delay before each character begins to jiggle:</p>

<pre><code class="language-css">.toon-char:nth-child(4n) { --base-rotate: -2deg; }
.toon-char:nth-child(4n+1) { --base-rotate: -4deg; }
.toon-char:nth-child(4n+2) { --base-rotate: 2deg; }
.toon-char:nth-child(4n+3) { --base-rotate: 4deg; }

.toon-char:nth-child(4n) { animation-delay: 0.1s; }
.toon-char:nth-child(4n+1) { animation-delay: 0.3s; }
.toon-char:nth-child(4n+2) { animation-delay: 0.5s; }
.toon-char:nth-child(4n+3) { animation-delay: 0.7s; }
</code></pre> 














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="317"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png"
			
			sizes="100vw"
			alt="Example from Andy&#39;s Toon Text collection."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      See this example in my Toon Text collection. (<a href='https://files.smashing.media/articles/smashing-animations-part-7-recreating-toon-text-css-svg/15-toon-text-collection.png'>Large preview</a>)
    </figcaption>
  
</figure>

<div class="partners__lead-place"></div>

<h2 id="one-frame-to-make-an-impression">One Frame To Make An Impression</h2>

<p>Cartoon title artists had one frame to make an impression, and their typography was as important as the artwork they painted. The same is true on the web.</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aA%20well-designed%20header%20or%20hero%20area%20needs%20clarity,%20character,%20and%20confidence%20%e2%80%94%20not%20simply%20a%20faded%20full-width%20background%20image.%0a&url=https://smashingmagazine.com%2f2025%2f12%2fsmashing-animations-part-7-recreating-toon-text-css-svg%2f">
      
A well-designed header or hero area needs clarity, character, and confidence — not simply a faded full-width background image.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<p>With a few carefully chosen CSS properties &mdash; shadows, strokes, clipped backgrounds, and some restrained animation &mdash; we can recreate that same impact. I love toon text not because I’m nostalgic, but because its design is intentional. Make deliberate choices, and let a little toon text typography add punch to your designs.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Brecht De Ruyte</author><title>State, Logic, And Native Power: CSS Wrapped 2025</title><link>https://www.smashingmagazine.com/2025/12/state-logic-native-power-css-wrapped-2025/</link><pubDate>Tue, 09 Dec 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/12/state-logic-native-power-css-wrapped-2025/</guid><description>CSS Wrapped 2025 is out! We’re entering a world where CSS can increasingly handle logic, state, and complex interactions once reserved for JavaScript. It’s no longer just about styling documents, but about crafting dynamic, ergonomic, and robust applications with a native toolkit more powerful than ever. Here’s an unpacking of the highlights and how they connect to the broader evolution of modern CSS.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/12/state-logic-native-power-css-wrapped-2025/" />
              <title>State, Logic, And Native Power: CSS Wrapped 2025</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>State, Logic, And Native Power: CSS Wrapped 2025</h1>
                  
                    
                    <address>Brecht De Ruyte</address>
                  
                  <time datetime="2025-12-09T10:00:00&#43;00:00" class="op-published">2025-12-09T10:00:00+00:00</time>
                  <time datetime="2025-12-09T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>If I were to divide CSS evolutions into categories, we have moved far beyond the days when we simply asked for <code>border-radius</code> to feel like we were living in the future. We are currently living in a moment where the platform is handing us tools that don’t just tweak the visual layer, but fundamentally redefine how we architect interfaces. I thought the number of features announced in 2024 couldn’t be topped. I’ve never been so happily wrong.</p>

<p>The Chrome team’s “<a href="https://chrome.dev/css-wrapped-2025/"><strong>CSS Wrapped 2025</strong></a>” is not just a list of features; it is a manifesto for a dynamic, native web. As someone who has spent a couple of years documenting these evolutions &mdash; from <a href="https://www.smashingmagazine.com/2024/08/css5-era-evolution/">defining “CSS5” eras</a> to the intricacies of <a href="https://www.smashingmagazine.com/2024/05/modern-css-layouts-no-framework/">modern layout utilities</a> &mdash; I find myself looking at this year’s wrap-up with a huge sense of excitement. We are seeing a shift towards “Optimized Ergonomics” and “Next-gen interactions” that allow us to stop fighting the code and start sculpting interfaces in their natural state.</p>

<p>In this article, you can find <strong>a comprehensive look at the standout features from Chrome’s report</strong>, viewed through the lens of my recent experiments and hopes for the future of the platform.</p>

<h2 id="the-component-revolution-finally-a-native-customizable-select">The Component Revolution: Finally, A Native Customizable Select</h2>

<p>For years, we have relied on heavy JavaScript libraries to style dropdowns, a “decades-old problem” that the platform has finally solved. As I detailed in <a href="https://utilitybend.com/blog/the-customizable-select-part-one-history-trickery-and-styling-the-select-with-css">my deep dive into the history of the customizable select</a> (and related articles), this has been a long road involving <a href="https://open-ui.org/">Open UI</a>, bikeshedding names like <code>&lt;selectmenu&gt;</code> and <code>&lt;selectlist&gt;</code>, and finally landing on a solution that re-uses the existing <code>&lt;select&gt;</code> element.</p>

<p>The introduction of <code>appearance: base-select</code> is a strong foundation. It allows us to fully customize the <code>&lt;select&gt;</code> element &mdash; including the button and the dropdown list (via <code>::picker(select)</code>) &mdash; using standard CSS. Crucially, this is built with progressive enhancement in mind. By wrapping our styles in a feature query, we ensure a seamless experience across all browsers.</p>

<p>We can opt in to this new behavior without breaking older browsers:</p>

<pre><code class="language-css">select {
  /&#42; Opt-in for the new customizable select &#42;/
  @supports (appearance: base-select) {
    &, &::picker(select) {
      appearance: base-select;
    }
  }
}
</code></pre>

<p>The fantastic addition to allow rich content inside options, such as images or flags, is a lot of fun. We can create all sorts of selects nowadays:</p>

<ul>
<li><strong>Demo:</strong> I created a <a href="https://codepen.io/utilitybend/pen/ByawgNN">Poké-adventure demo</a> showing how the new <code>&lt;selectedcontent&gt;</code> element can clone rich content (like a Pokéball icon) from an option directly into the button.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="JoXwwoZ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [A customizable select with images inside of the options and the selectedcontent [forked]](https://codepen.io/smashingmag/pen/JoXwwoZ) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/JoXwwoZ">A customizable select with images inside of the options and the selectedcontent [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<ul>
<li><strong>Demo:</strong> A comprehensive look at <a href="https://codepen.io/utilitybend/pen/GgRrLWb">styling the select with only pseudo-elements</a>.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="pvyqqJR"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [A customizable select with only pseudo-elements [forked]](https://codepen.io/smashingmag/pen/pvyqqJR) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/pvyqqJR">A customizable select with only pseudo-elements [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<ul>
<li><strong>Demo:</strong> Or you can kick it up a notch with this <a href="https://codepen.io/utilitybend/pen/ByoBMBm">Menu selection demo using optgroups</a>.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="myPaaJZ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [An actual Select Menu with optgroups [forked]](https://codepen.io/smashingmag/pen/myPaaJZ) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/myPaaJZ">An actual Select Menu with optgroups [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<p>This feature alone signals a massive shift in how we will build forms, reducing dependencies and technical debt.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="scroll-markers-and-the-death-of-the-javascript-carousel">Scroll Markers And The Death Of The JavaScript Carousel</h2>

<p>Creating carousels has historically been a friction point between developers and clients. Clients love them, developers dread the JavaScript required to make them accessible and performant. The arrival of <code>::scroll-marker</code> and <code>::scroll-button()</code> pseudo-elements changes this dynamic entirely.</p>

<p>These features allow us to create navigation dots and scroll buttons purely with CSS, linked natively to the scroll container. As I wrote on my blog, this was <a href="https://utilitybend.com/blog/love-at-first-slide-creating-a-carousel-purely-out-of-css">Love at first slide</a>. The ability to create a fully functional, accessible slider without a single line of JavaScript is not just convenient; it is a triumph for performance. There are some accessibility concerns around this feature, and even though these are valid, there might be a way for us developers to make it work. The good thing is, all these UI changes are making it a lot easier than custom DOM manipulation and dragging around aria tags, but I digress…</p>

<p>We can now group markers automatically using <code>scroll-marker-group</code> and style the buttons using anchor positioning to place them exactly where we want.</p>

<div class="break-out">
<pre><code class="language-css">.carousel {
  overflow-x: auto;
  scroll-marker-group: after; /&#42; Creates the container for dots &#42;/

  /&#42; Create the buttons &#42;/
  &::scroll-button(inline-end),
  &::scroll-button(inline-start) {
    content: " ";
    position: absolute;
    /&#42; Use anchor positioning to center them &#42;/
    position-anchor: --carousel;
    top: anchor(center);
  }

  /&#42; Create the markers on the children &#42;/
  div {
    &::scroll-marker {
      content: " ";
      width: 24px;
      border-radius: 50%;
      cursor: pointer;
    }
    /&#42; Highlight the active marker &#42;/
    &::scroll-marker:target-current {
      background: white;
    }
  }
}
</code></pre>
</div>

<ul>
<li><strong>Demo:</strong> My experiment creating a <a href="https://codepen.io/utilitybend/pen/vEBQxNb">carousel purely out of HTML and CSS</a>, using anchor positioning to place the buttons.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="ogxJJjQ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Carousel Pure HTML and CSS [forked]](https://codepen.io/smashingmag/pen/ogxJJjQ) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/ogxJJjQ">Carousel Pure HTML and CSS [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<ul>
<li><strong>Demo:</strong> A <a href="https://codepen.io/utilitybend/pen/bNbXZWb">Webshop slick slider remake</a> using <code>attr()</code> to pull background images dynamically into the markers.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="gbrZZPY"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Webshop slick slider remake in CSS [forked]](https://codepen.io/smashingmag/pen/gbrZZPY) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/gbrZZPY">Webshop slick slider remake in CSS [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<h2 id="state-queries-sticky-thing-stuck-snappy-thing-snapped">State Queries: Sticky Thing Stuck? Snappy Thing Snapped?</h2>

<p>For a long time, we have lacked the ability to know if a <a href="https://utilitybend.com/blog/is-the-sticky-thing-stuck-is-the-snappy-item-snapped-a-look-at-state-queries-in-css">“sticky thing is stuck” or if a “snappy item is snapped”</a> without relying on IntersectionObserver hacks. Chrome 133 introduced scroll-state queries, allowing us to query these states declaratively.</p>

<p>By setting <code>container-type: scroll-state</code>, we can now style children based on whether they are stuck, snapped, or overflowing. This is a massive “quality of life” improvement that I have been eagerly waiting for since CSS Day 2023. It has even evolved a lot since we can also see the direction of the scroll, lovely!</p>

<p>For a simple example: we can finally apply a shadow to a header <em>only</em> when it is actually sticking to the top of the viewport:</p>

<pre><code class="language-css">.header-container {
  container-type: scroll-state;
  position: sticky;
  top: 0;

  header {
    transition: box-shadow 0.5s ease-out;
    /&#42; The query checks the state of the container &#42;/
    @container scroll-state(stuck: top) {
      box-shadow: rgba(0, 0, 0, 0.6) 0px 12px 28px 0px;
    }
  }
}
</code></pre>

<ul>
<li><strong>Demo:</strong> A <a href="https://codepen.io/utilitybend/pen/XWLQPOe">sticky header</a> that only applies a shadow when it is actually stuck.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="raeooxY"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Sticky headers with scroll-state query, checking if the sticky element is stuck [forked]](https://codepen.io/smashingmag/pen/raeooxY) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/raeooxY">Sticky headers with scroll-state query, checking if the sticky element is stuck [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<ul>
<li><strong>Demo:</strong> A <a href="https://codepen.io/utilitybend/pen/MWMZoqp">Pokémon-themed list</a> that uses scroll-state queries combined with anchor positioning to move a frame over the currently snapped character.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="vEGvvLM"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Scroll-state query to check which item is snapped with CSS, Pokemon version [forked]](https://codepen.io/smashingmag/pen/vEGvvLM) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/vEGvvLM">Scroll-state query to check which item is snapped with CSS, Pokemon version [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<div class="partners__lead-place"></div>

<h2 id="optimized-ergonomics-logic-in-css">Optimized Ergonomics: Logic In CSS</h2>

<p>The “Optimized Ergonomics” section of CSS Wrapped highlights features that make our workflows more intuitive. Three features stand out as transformative for how we write logic:</p>

<ol>
<li><strong><code>if()</code> Statements</strong><br />
We are finally getting conditionals in CSS. The <code>if()</code> function acts like a ternary operator for stylesheets, allowing us to apply values based on media, support, or style queries inline. This reduces the need for verbose <code>@media</code> blocks for single property changes.</li>
<li><strong><code>@function</code> functions</strong><br />
We can finally move some logic to a different place, resulting in some cleaner files, a real quality of life feature.</li>
<li><strong><code>sibling-index()</code> and <code>sibling-count()</code></strong><br />
These tree-counting functions solve the issue of staggering animations or styling items based on list size. As I explored in <a href="https://utilitybend.com/blog/styling-siblings-with-css-has-never-been-easier-experimenting-with-sibling-count-and-sibling-index">Styling siblings with CSS has never been easier</a>, this eliminates the need to hard-code custom properties (like <code>--index: 1</code>) in our HTML.</li>
</ol>

<h3 id="example-calculating-layouts">Example: Calculating Layouts</h3>

<p>We can now write concise mathematical formulas. For example, staggering an animation for cards entering the screen becomes trivial:</p>

<pre><code class="language-css">.card-container &gt; &#42; {
  animation: reveal 0.6s ease-out forwards;
  /&#42; No more manual --index variables! &#42;/
  animation-delay: calc(sibling-index() &#42; 0.1s);
}
</code></pre>

<p>I even experimented with using these functions along with trigonometry to place items in a perfect circle without any JavaScript.</p>

<ul>
<li><strong>Demo:</strong> <a href="https://codepen.io/utilitybend/pen/wBKQPLr">Staggering card animations dynamically</a>.</li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="RNaEERz"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [Stagger cards using sibling-index() [forked]](https://codepen.io/smashingmag/pen/RNaEERz) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/RNaEERz">Stagger cards using sibling-index() [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<ul>
<li><strong>Demo:</strong> Placing items in a <a href="https://codepen.io/utilitybend/pen/VYvVXLN">perfect circle</a> using <code>sibling-index</code>, <code>sibling-count</code>, and the new CSS <code>@function</code> feature.
<br /></li>
</ul>

<figure class="break-out">
	<p data-height="480"
	data-theme-id="light"
	data-slug-hash="XJdoojZ"
	data-user="smashingmag"
	data-default-tab="result"
	class="codepen">See the Pen [The circle using sibling-index, sibling-count and functions [forked]](https://codepen.io/smashingmag/pen/XJdoojZ) by <a href="https://codepen.io/utilitybend">utilitybend</a>.</p>
	<figcaption>See the Pen <a href="https://codepen.io/smashingmag/pen/XJdoojZ">The circle using sibling-index, sibling-count and functions [forked]</a> by <a href="https://codepen.io/utilitybend">utilitybend</a>.</figcaption>
</figure>

<h2 id="my-css-to-do-list-features-i-can-t-wait-to-try">My CSS To-Do List: Features I Can’t Wait To Try</h2>

<p>While I have been busy sculpting selects and transitions, the “CSS Wrapped 2025” report is packed with other goodies that I haven’t had the chance to fire up in CodePen yet. These are high on my list for my next experiments:</p>

<h3 id="anchored-container-queries">Anchored Container Queries</h3>

<p>I used CSS Anchor Positioning for the buttons in my carousel demo, but “CSS Wrapped” highlights an evolution of this: <strong>Anchored Container Queries</strong>. This solves a problem we’ve all had with tooltips: if the browser flips the tooltip from top to bottom because of space constraints, the “arrow” often stays pointing the wrong way. With anchored container queries (<code>@container anchored(fallback: flip-block)</code>), we can style the element based on which fallback position the browser actually chose.</p>

<h3 id="nested-view-transition-groups">Nested View Transition Groups</h3>

<p>View Transitions have been a revolution, but they came with a specific trade-off: they flattened the element tree, which often broke 3D transforms or overflow: clip. I always had a feeling that it was missing something, and this might just be the answer. By using <code>view-transition-group: nearest</code>, we can finally nest transition groups within each other.</p>

<p>This allows us to maintain clipping effects or 3D rotations during a transition &mdash; something that was previously impossible because the elements were hoisted up to the top level.</p>

<pre><code class="language-css">.card img {
  view-transition-name: photo;
  view-transition-group: nearest; /&#42; Keep it nested! &#42;/
}
</code></pre>

<h3 id="typography-and-shapes">Typography and Shapes</h3>

<p>Finally, the ergonomist in me is itching to try <strong>Text Box Trim</strong>, which promises to remove that annoying extra whitespace above and below text content (the leading) to finally achieve perfect vertical alignment. And for the creative side, <code>corner-shape</code> and the <code>shape()</code> function are opening up non-rectangular layouts, allowing for “squaricles” and complex paths that respond to CSS variables. That being said, I can’t wait to have a design full of squircles!</p>

<div class="partners__lead-place"></div>

<h2 id="a-hopeful-future">A Hopeful Future</h2>

<p>We are witnessing a world where <strong>CSS is becoming capable of handling logic, state, and complex interactions that previously belonged to JavaScript</strong>. Features like <code>moveBefore</code> (preserving DOM state for iframes/videos) and <code>attr()</code> (using types beyond strings for colors and grids) further cement this reality.</p>

<p>While some of these features are currently experimental or specific to Chrome, the momentum is undeniable. We must hope for continued support across all browsers through initiatives like Interop to ensure these capabilities become the baseline. That being said, having browser engines is just as important as having all these awesome features in “Chrome first”. These new features need to be discussed, tinkered with, and tested before ever landing in browsers.</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aIt%20is%20a%20fantastic%20moment%20to%20get%20into%20CSS.%20We%20are%20no%20longer%20just%20styling%20documents;%20we%20are%20crafting%20dynamic,%20ergonomic,%20and%20robust%20applications%20with%20a%20native%20toolkit%20that%20is%20more%20powerful%20than%20ever.%0a&url=https://smashingmagazine.com%2f2025%2f12%2fstate-logic-native-power-css-wrapped-2025%2f">
      
It is a fantastic moment to get into CSS. We are no longer just styling documents; we are crafting dynamic, ergonomic, and robust applications with a native toolkit that is more powerful than ever.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<p>Let’s get going with this new era and spread the word.</p>

<p>This is <a href="https://chrome.dev/css-wrapped-2025/">CSS Wrapped</a>!</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Victor Yocco</author><title>Beyond The Black Box: Practical XAI For UX Practitioners</title><link>https://www.smashingmagazine.com/2025/12/beyond-black-box-practical-xai-ux-practitioners/</link><pubDate>Fri, 05 Dec 2025 15:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/12/beyond-black-box-practical-xai-ux-practitioners/</guid><description>Explainable AI isn’t just a challenge for data scientists. It’s also a design challenge and a core pillar of trustworthy, effective AI products. Victor Yocco offers practical guidance and design patterns for building explainability into real products.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/12/beyond-black-box-practical-xai-ux-practitioners/" />
              <title>Beyond The Black Box: Practical XAI For UX Practitioners</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Beyond The Black Box: Practical XAI For UX Practitioners</h1>
                  
                    
                    <address>Victor Yocco</address>
                  
                  <time datetime="2025-12-05T15:00:00&#43;00:00" class="op-published">2025-12-05T15:00:00+00:00</time>
                  <time datetime="2025-12-05T15:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>In my <a href="https://www.smashingmagazine.com/2025/09/psychology-trust-ai-guide-measuring-designing-user-confidence/">last piece</a>, we established a foundational truth: for users to adopt and rely on AI, they must <strong>trust</strong> it. We talked about trust being a multifaceted construct, built on perceptions of an AI’s <strong>Ability</strong>, <strong>Benevolence</strong>, <strong>Integrity</strong>, and <strong>Predictability</strong>. But what happens when an AI, in its silent, algorithmic wisdom, makes a decision that leaves a user confused, frustrated, or even hurt? A mortgage application is denied, a favorite song is suddenly absent from a playlist, and a qualified resume is rejected before a human ever sees it. In these moments, ability and predictability are shattered, and benevolence feels a world away.</p>

<p>Our conversation now  must evolve from the <em>why</em> of trust to the <em>how</em> of transparency. The field of <strong>Explainable AI (XAI)</strong>, which focuses on developing methods to make AI outputs understandable to humans, has emerged to address this, but it’s often framed as a purely technical challenge for data scientists. I argue it’s a critical design challenge for products relying on AI. It’s our job as UX professionals to bridge the gap between algorithmic decision-making and human understanding.</p>

<p>This article provides practical, actionable guidance on how to research and design for explainability. We’ll move beyond the buzzwords and into the mockups, translating complex XAI concepts into concrete design patterns you can start using today.</p>

<h2 id="de-mystifying-xai-core-concepts-for-ux-practitioners">De-mystifying XAI: Core Concepts For UX Practitioners</h2>

<p>XAI is about answering the user’s question: “<strong>Why?</strong>” Why was I shown this ad? Why is this movie recommended to me? Why was my request denied? Think of it as the AI showing its work on a math problem. Without it, you just have an answer, and you’re forced to take it on faith. In showing the steps, you build comprehension and trust. You also allow for your work to be double-checked and verified by the very humans it impacts.</p>

<h3 id="feature-importance-and-counterfactuals">Feature Importance And Counterfactuals</h3>

<p>There are a number of techniques we can use to clarify or explain what is happening with AI. While methods range from providing the entire logic of a decision tree to generating natural language summaries of an output, two of the most practical and impactful types of information UX practitioners can introduce into an experience are <strong>feature importance</strong> (Figure 1) and <strong>counterfactuals</strong>. These are often the most straightforward for users to understand and the most actionable for designers to implement.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="478"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png"
			
			sizes="100vw"
			alt="A fictional example of feature importance"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Figure 1: A fictional example of feature importance where a bank system shows the importance of various features that lead to a model’s decision. Image generated using Google Gemini. (<a href='https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/1-example-feature-importance.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h4 id="feature-importance">Feature Importance</h4>

<p>This explainability method answers, “<strong>What were the most important factors the AI considered?</strong>” It’s about identifying the top 2-3 variables that had the biggest impact on the outcome. It’s the headline, not the whole story.</p>

<blockquote><strong>Example</strong>: Imagine an AI that predicts whether a customer will churn (cancel their service). Feature importance might reveal that “number of support calls in the last month” and “recent price increases” were the two most important factors in determining if a customer was likely to churn.</blockquote>

<h4 id="counterfactuals">Counterfactuals</h4>

<p>This powerful method answers, “<strong>What would I need to change to get a different outcome?</strong>” This is crucial because it gives users a sense of agency. It transforms a frustrating “no” into an actionable “not yet.”</p>

<blockquote><strong>Example</strong>: Imagine a loan application system that uses AI. A user is denied a loan. Instead of just seeing “Application Denied,” a counterfactual explanation would also share, “If your credit score were 50 points higher, or if your debt-to-income ratio were 10% lower, your loan would have been approved.” This gives Sarah clear, actionable steps she can take to potentially get a loan in the future.</blockquote>

<h3 id="using-model-data-to-enhance-the-explanation">Using Model Data To Enhance The Explanation</h3>

<p>Although technical specifics are often handled by data scientists, it&rsquo;s helpful for UX practitioners to know that tools like <a href="https://www.geeksforgeeks.org/artificial-intelligence/introduction-to-explainable-aixai-using-lime/">LIME</a> (Local Interpretable Model-agnostic Explanations) which explains individual predictions by approximating the model locally, and <a href="https://shap.readthedocs.io/en/latest/example_notebooks/overviews/An%20introduction%20to%20explainable%20AI%20with%20Shapley%20values.html">SHAP</a> (SHapley Additive exPlanations) which uses a game theory approach to explain the output of any machine learning model are commonly used to extract these “why” insights from complex models. These libraries essentially help break down an AI’s decision to show which inputs were most influential for a given outcome.</p>

<p>When done properly, the data underlying an AI tool’s decision can be used to tell a powerful story. Let’s walk through feature importance and counterfactuals and show how the data science behind the decision can be utilized to enhance the user’s experience.</p>

<p>Now let’s cover feature importance with the assistance of <strong>Local Explanations (e.g., LIME)</strong> data: This approach answers, “<strong>Why did the AI make <em>this specific</em> recommendation for me, right now?</strong>” Instead of a general explanation of how the model works, it provides a focused reason for a single, specific instance. It’s personal and contextual.</p>

<blockquote><strong>Example</strong>: Imagine an AI-powered music recommendation system like Spotify. A local explanation would answer, “Why did the system recommend <strong>this specific</strong> song by Adele to <strong>you</strong> right now?” The explanation might be: “Because you recently listened to several other emotional ballads and songs by female vocalists.”</blockquote>

<p>Finally, let’s cover the inclusion of <strong>Value-based Explanations (e.g. Shapley Additive Explanations (SHAP)</strong> data to an explanation of a decision: This is a more nuanced version of feature importance that answers, “<strong>How did each factor push the decision one way or the other?</strong>” It helps visualize <em>what</em> mattered, and whether its influence was positive or negative.</p>

<blockquote><strong>Example</strong>: Imagine a bank uses an AI model to decide whether to approve a loan application.</blockquote>

<p><strong>Feature Importance</strong>: The model output might show that the applicant’s credit score, income, and debt-to-income ratio were the most important factors in its decision. This answers <em>what</em> mattered.</p>

<p><strong>Feature Importance with Value-Based Explanations (SHAP)</strong>: SHAP values would take feature importance further based on elements of the model.</p>

<ul>
<li>For an approved loan, SHAP might show that a high credit score significantly <em>pushed</em> the decision towards approval (positive influence), while a slightly higher-than-average debt-to-income ratio <em>pulled</em> it slightly away (negative influence), but not enough to deny the loan.</li>
<li>For a denied loan, SHAP could reveal that a low income and a high number of recent credit inquiries <em>strongly pushed</em> the decision towards denial, even if the credit score was decent.</li>
</ul>

<p>This helps the loan officer explain to the applicant beyond <em>what</em> was considered, to <em>how each factor contributed</em> to the final “yes” or “no” decision.</p>

<p>It’s crucial to recognize that the ability to provide good explanations often starts much earlier in the development cycle. Data scientists and engineers play a pivotal role by intentionally structuring models and data pipelines in ways that inherently support explainability, rather than trying to bolt it on as an afterthought.</p>

<p>Research and design teams can foster this by initiating early conversations with data scientists and engineers about user needs for understanding, contributing to the development of explainability metrics, and collaboratively prototyping explanations to ensure they are both accurate and user-friendly.</p>

<h2 id="xai-and-ethical-ai-unpacking-bias-and-responsibility">XAI And Ethical AI: Unpacking Bias And Responsibility</h2>

<p>Beyond building trust, XAI plays a critical role in addressing the profound <strong>ethical implications of AI</strong>*, particularly concerning algorithmic bias. Explainability techniques, such as analyzing SHAP values, can reveal if a model’s decisions are disproportionately influenced by sensitive attributes like race, gender, or socioeconomic status, even if these factors were not explicitly used as direct inputs.</p>

<p>For instance, if a loan approval model consistently assigns negative SHAP values to applicants from a certain demographic, it signals a potential bias that needs investigation, empowering teams to surface and mitigate such unfair outcomes.</p>

<p>The power of XAI also comes with the potential for “<strong>explainability washing</strong>.” Just as “greenwashing” misleads consumers about environmental practices, explainability washing can occur when explanations are designed to obscure, rather than illuminate, problematic algorithmic behavior or inherent biases. This could manifest as overly simplistic explanations that omit critical influencing factors, or explanations that strategically frame results to appear more neutral or fair than they truly are. It underscores the ethical responsibility of UX practitioners to design explanations that are genuinely transparent and verifiable.</p>

<p>UX professionals, in collaboration with data scientists and ethicists, hold a crucial responsibility in communicating the <em>why</em> of a decision, and also the limitations and potential biases of the underlying AI model. This involves setting realistic user expectations about AI accuracy, identifying where the model might be less reliable, and providing clear channels for recourse or feedback when users perceive unfair or incorrect outcomes. Proactively addressing these ethical dimensions will allow us to build AI systems that are truly just and trustworthy.</p>

<h2 id="from-methods-to-mockups-practical-xai-design-patterns">From Methods To Mockups: Practical XAI Design Patterns</h2>

<p>Knowing the concepts is one thing; designing them is another. Here’s how we can translate these XAI methods into intuitive design patterns.</p>

<h3 id="pattern-1-the-because-statement-for-feature-importance">Pattern 1: The &ldquo;Because&rdquo; Statement (for Feature Importance)</h3>

<p>This is the simplest and often most effective pattern. It’s a direct, plain-language statement that surfaces the primary reason for an AI’s action.</p>

<ul>
<li><strong>Heuristic</strong>: Be direct and concise. Lead with the single most impactful reason. Avoid jargon at all costs.</li>
</ul>

<blockquote><strong>Example</strong>: Imagine a music streaming service. Instead of just presenting a “Discover Weekly” playlist, you add a small line of microcopy.<br /><br /><strong>Song Recommendation</strong>: “Velvet Morning”<br />Because you listen to “The Fuzz” and other psychedelic rock.</blockquote>

<h3 id="pattern-2-the-what-if-interactive-for-counterfactuals">Pattern 2: The &ldquo;What-If&rdquo; Interactive (for Counterfactuals)</h3>

<p>Counterfactuals are inherently about empowerment. The best way to represent them is by giving users interactive tools to explore possibilities themselves. This is perfect for financial, health, or other goal-oriented applications.</p>

<ul>
<li><strong>Heuristic</strong>: Make explanations interactive and empowering. Let users see the cause and effect of their choices.</li>
</ul>

<blockquote><strong>Example</strong>: A loan application interface. After a denial, instead of a dead end, the user gets a tool to determine how various scenarios (what-ifs) might play out (See Figure 1).</blockquote>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="582"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png"
			
			sizes="100vw"
			alt="An example of Counterfactuals"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Figure 2: An example of Counterfactuals using a what-if scenario, letting the user see how changing different values of the model’s features can impact outcomes. Image generated using Google Gemini. (<a href='https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/2-example-counterfactuals.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h3 id="pattern-3-the-highlight-reel-for-local-explanations">Pattern 3: The Highlight Reel (For Local Explanations)</h3>

<p>When an AI performs an action on a user’s content (like summarizing a document or identifying faces in photos), the explanation should be visually linked to the source.</p>

<ul>
<li><strong>Heuristic</strong>: Use visual cues like highlighting, outlines, or annotations to connect the explanation directly to the interface element it’s explaining.</li>
</ul>

<blockquote><strong>Example</strong>: An AI tool that summarizes long articles.<br /><br /><strong>AI-Generated Summary Point</strong>:<br />Initial research showed a market gap for sustainable products.<br /><br /><strong>Source in Document</strong>:<br />“...Our Q2 analysis of market trends conclusively demonstrated that <strong>no major competitor was effectively serving the eco-conscious consumer, revealing a significant market gap for sustainable products</strong>...”</blockquote>

<h3 id="pattern-4-the-push-and-pull-visual-for-value-based-explanations">Pattern 4: The Push-and-Pull Visual (for Value-based Explanations)</h3>

<p>For more complex decisions, users might need to understand the interplay of factors. Simple data visualizations can make this clear without being overwhelming.</p>

<ul>
<li><strong>Heuristic</strong>: Use simple, color-coded data visualizations (like bar charts) to show the factors that positively and negatively influenced a decision.</li>
</ul>

<blockquote><strong>Example</strong>: An AI screening a candidate’s profile for a job.<br /><br />Why this candidate is a 75% match:<br /><br /><strong>Factors pushing the score up</strong>:<br /><ul><li>5+ Years UX Research Experience</li><li>Proficient in Python</li></ul><br /><strong>Factors pushing the score down</strong>:<br /><ul><li>No experience with B2B SaaS</li></ul></blockquote>

<p>Learning and using these design patterns in the UX of your AI product will help increase the explainability. You can also use additional techniques that I’m not covering in-depth here. This includes the following:</p>

<ul>
<li><strong>Natural language explanations</strong>: Translating an AI’s technical output into simple, conversational human language that non-experts can easily understand.</li>
<li><strong>Contextual explanations</strong>: Providing a rationale for an AI’s output at the specific moment and location, it is most relevant to the user’s task.</li>
<li><strong>Relevant visualizations</strong>: Using charts, graphs, or heatmaps to visually represent an AI’s decision-making process, making complex data intuitive and easier for users to grasp.</li>
</ul>

<p><strong>A Note For the Front End</strong>: <em>Translating these explainability outputs into seamless user experiences also presents its own set of technical considerations. Front-end developers often grapple with API design to efficiently retrieve explanation data, and performance implications (like the real-time generation of explanations for every user interaction) need careful planning to avoid latency.</em></p>

<h2 id="some-real-world-examples">Some Real-world Examples</h2>

<p><strong>UPS Capital’s DeliveryDefense</strong></p>

<p>UPS uses AI to assign a “delivery confidence score” to addresses to predict the likelihood of a package being stolen. Their <a href="https://about.ups.com/us/en/our-stories/innovation-driven/ups-s-deliverydefense-pits-ai-against-criminals.html">DeliveryDefense</a> software analyzes historical data on location, loss frequency, and other factors. If an address has a low score, the system can proactively reroute the package to a secure UPS Access Point, providing an explanation for the decision (e.g., “Package rerouted to a secure location due to a history of theft”). This system demonstrates how XAI can be used for risk mitigation and building customer trust through transparency.</p>

<p><strong>Autonomous Vehicles</strong></p>

<p>These vehicles of the future will need to effectively use <a href="https://online.hbs.edu/blog/post/ai-in-business">XAI to help their vehicles make safe, explainable decisions</a>. When a self-driving car brakes suddenly, the system can provide a real-time explanation for its action, for example, by identifying a pedestrian stepping into the road. This is not only crucial for passenger comfort and trust but is a regulatory requirement to prove the safety and accountability of the AI system.</p>

<p><strong>IBM Watson Health (and its challenges)</strong></p>

<p>While often cited as a general example of AI in healthcare, it’s also a valuable case study for the <em>importance</em> of XAI. The <a href="https://www.henricodolfing.com/2024/12/case-study-ibm-watson-for-oncology-failure.html">failure of its Watson for Oncology project</a> highlights what can go wrong when explanations are not clear, or when the underlying data is biased or not localized. The system’s recommendations were sometimes inconsistent with local clinical practices because they were based on U.S.-centric guidelines. This serves as a cautionary tale on the need for robust, context-aware explainability.</p>

<h2 id="the-ux-researcher-s-role-pinpointing-and-validating-explanations">The UX Researcher’s Role: Pinpointing And Validating Explanations</h2>

<p>Our design solutions are only effective if they address the right user questions at the right time. An explanation that answers a question the user doesn’t have is just noise. This is where UX research becomes the critical connective tissue in an XAI strategy, ensuring that we explain the what and how that actually matters to our users. The researcher’s role is twofold: first, to inform the strategy by identifying where explanations are needed, and second, to validate the designs that deliver those explanations.</p>

<h3 id="informing-the-xai-strategy-what-to-explain">Informing the XAI Strategy (What to Explain)</h3>

<p>Before we can design a single explanation, we must understand the user’s mental model of the AI system. What do they believe it’s doing? Where are the gaps between their understanding and the system’s reality? This is the foundational work of a UX researcher.</p>

<h4 id="mental-model-interviews-unpacking-user-perceptions-of-ai-systems">Mental Model Interviews: Unpacking User Perceptions Of AI Systems</h4>

<p>Through deep, semi-structured interviews, UX practitioners can gain invaluable insights into how users perceive and understand AI systems. These sessions are designed to encourage users to literally draw or describe their internal “mental model” of how they believe the AI works. This often involves asking open-ended questions that prompt users to explain the system’s logic, its inputs, and its outputs, as well as the relationships between these elements.</p>

<p>These interviews are powerful because they frequently reveal profound misconceptions and assumptions that users hold about AI. For example, a user interacting with a recommendation engine might confidently assert that the system is based purely on their past viewing history. They might not realize that the algorithm also incorporates a multitude of other factors, such as the time of day they are browsing, the current trending items across the platform, or even the viewing habits of similar users.</p>

<p>Uncovering this gap between a user’s mental model and the actual underlying AI logic is critically important. It tells us precisely what specific information we need to communicate to users to help them build a more accurate and robust mental model of the system. This, in turn, is a fundamental step in fostering trust. When users understand, even at a high level, how an AI arrives at its conclusions or recommendations, they are more likely to trust its outputs and rely on its functionality.</p>

<h4 id="ai-journey-mapping-a-deep-dive-into-user-trust-and-explainability">AI Journey Mapping: A Deep Dive Into User Trust And Explainability</h4>

<p>By meticulously mapping the user’s journey with an AI-powered feature, we gain invaluable insights into the precise moments where confusion, frustration, or even profound distrust emerge. This uncovers critical junctures where the user’s mental model of how the AI operates clashes with its actual behavior.</p>

<p>Consider a music streaming service: Does the user’s trust plummet when a playlist recommendation feels “random,” lacking any discernible connection to their past listening habits or stated preferences? This perceived randomness is a direct challenge to the user’s expectation of intelligent curation and a breach of the implicit promise that the AI understands their taste. Similarly, in a photo management application, do users experience significant frustration when an AI photo-tagging feature consistently misidentifies a cherished family member? This error is more than a technical glitch; it strikes at the heart of accuracy, personalization, and even emotional connection.</p>

<p>These pain points are vivid signals indicating precisely where a well-placed, clear, and concise explanation is necessary. Such explanations serve as crucial repair mechanisms, mending a breach of trust that, if left unaddressed, can lead to user abandonment.</p>

<p>The power of AI journey mapping lies in its ability to move us beyond simply explaining the final output of an AI system. While understanding <em>what</em> the AI produced is important, it’s often insufficient. Instead, this process compels us to focus on explaining the <em>process</em> at critical moments. This means addressing:</p>

<ul>
<li><strong>Why a particular output was generated</strong>: Was it due to specific input data? A particular model architecture?</li>
<li><strong>What factors influenced the AI’s decision</strong>: Were certain features weighted more heavily?</li>
<li><strong>How the AI arrived at its conclusion</strong>: Can we offer a simplified, analogous explanation of its internal workings?</li>
<li><strong>What assumptions the AI made</strong>: Were there implicit understandings of the user’s intent or data that need to be surfaced?</li>
<li><strong>What the limitations of the AI are</strong>: Clearly communicating what the AI <em>cannot</em> do, or where its accuracy might waver, builds realistic expectations.</li>
</ul>

<p>AI journey mapping transforms the abstract concept of XAI into a practical, actionable framework for UX practitioners. It enables us to move beyond theoretical discussions of explainability and instead pinpoint the exact moments where user trust is at stake, providing the necessary insights to build AI experiences that are powerful, transparent, understandable, and trustworthy.</p>

<p>Ultimately, research is how we uncover the unknowns. Your team might be debating how to explain why a loan was denied, but research might reveal that users are far more concerned with understanding how their data was used in the first place. Without research, we are simply guessing what our users are wondering.</p>

<h2 id="collaborating-on-the-design-how-to-explain-your-ai">Collaborating On The Design (How to Explain Your AI)</h2>

<p>Once research has identified what to explain, the collaborative loop with design begins. Designers can prototype the patterns we discussed earlier—the “Because” statement, the interactive sliders—and researchers can put those designs in front of users to see if they hold up.</p>

<p><strong>Targeted Usability &amp; Comprehension Testing</strong>: We can design research studies that specifically test the XAI components. We don’t just ask, “*Is this easy to use?*” We ask, “*After seeing this, can you tell me in your own words why the system recommended this product?*” or “*Show me what you would do to see if you could get a different result.*” The goal here is to measure comprehension and actionability, alongside usability.</p>

<p><strong>Measuring Trust Itself</strong>: We can use simple surveys and rating scales before and after an explanation is shown. For instance, we can ask a user on a 5-point scale, “*How much do you trust this recommendation?*” before they see the “Because” statement, and then ask them again afterward. This provides quantitative data on whether our explanations are actually moving the needle on trust.</p>

<p>This process creates a powerful, iterative loop. Research findings inform the initial design. That design is then tested, and the new findings are fed back to the design team for refinement. Maybe the “Because” statement was too jargony, or the “What-If” slider was more confusing than empowering. Through this collaborative validation, we ensure that the final explanations are technically accurate, genuinely understandable, useful, and trust-building for the people using the product.</p>

<h2 id="the-goldilocks-zone-of-explanation">The Goldilocks Zone Of Explanation</h2>

<p>A critical word of caution: it is possible to <em>over-explain</em>. As in the fairy tale, where Goldilocks sought the porridge that was ‘just right’, the goal of a good explanation is to provide the right amount of detail—not too much and not too little. Bombarding a user with every variable in a model will lead to cognitive overload and can actually <em>decrease</em> trust. The goal is not to make the user a data scientist.</p>

<p>One solution is <strong>progressive disclosure</strong>.</p>

<ol>
<li><strong>Start with the simple.</strong> Lead with a concise “Because” statement. For most users, this will be enough.</li>
<li><strong>Offer a path to detail.</strong> Provide a clear, low-friction link like “Learn More” or “See how this was determined.”</li>
<li><strong>Reveal the complexity.</strong> Behind that link, you can offer the interactive sliders, the visualizations, or a more detailed list of contributing factors.</li>
</ol>

<p>This layered approach respects user attention and expertise, providing just the right amount of information for their needs. Let’s imagine you’re using a smart home device that recommends optimal heating based on various factors.</p>

<p><strong>Start with the simple</strong>: “*Your home is currently heated to 72 degrees, which is the optimal temperature for energy savings and comfort.*”</p>

<p><strong>Offer a path to detail</strong>: Below that, a small link or button: “<em>Why is 72 degrees optimal?</em>&ldquo;</p>

<p><strong>Reveal the complexity</strong>: Clicking that link could open a new screen showing:</p>

<ul>
<li>Interactive sliders for outside temperature, humidity, and your preferred comfort level, demonstrating how these adjust the recommended temperature.</li>
<li>A visualization of energy consumption at different temperatures.</li>
<li>A list of contributing factors like “Time of day,” “Current outside temperature,” “Historical energy usage,” and “Occupancy sensors.”</li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="449"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png"
			
			sizes="100vw"
			alt="An example of progressive disclosure in three stages"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Figure 3: An example of progressive disclosure in three stages: the simple details with an option to click for more details, more details with the option to understand what will happen if the user changes the settings. (<a href='https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/3-example-progressive-disclosure.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>It’s effective to combine multiple XAI methods and this Goldilocks Zone of Explanation pattern, which advocates for progressive disclosure, implicitly encourages this. You might start with a simple “Because” statement (Pattern 1) for immediate comprehension, and then offer a “Learn More” link that reveals a “What-If” Interactive (Pattern 2) or a “Push-and-Pull Visual” (Pattern 4) for deeper exploration.</p>

<p>For instance, a loan application system could initially state the primary reason for denial (feature importance), then allow the user to interact with a “What-If” tool to see how changes to their income or debt would alter the outcome (counterfactuals), and finally, provide a detailed “Push-and-Pull” chart (value-based explanation) to illustrate the positive and negative contributions of all factors. This layered approach allows users to access the level of detail they need, when they need it, preventing cognitive overload while still providing comprehensive transparency.</p>

<p>Determining which XAI tools and methods to use is primarily a function of thorough UX research. Mental model interviews and AI journey mapping are crucial for pinpointing user needs and pain points related to AI understanding and trust. Mental model interviews help uncover user misconceptions about how the AI works, indicating areas where fundamental explanations (like feature importance or local explanations) are needed. AI journey mapping, on the other hand, identifies critical moments of confusion or distrust in the user’s interaction with the AI, signaling where more granular or interactive explanations (like counterfactuals or value-based explanations) would be most beneficial to rebuild trust and provide agency.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="399"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png"
			
			sizes="100vw"
			alt="An example of a fictitious AI business startup assistant"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Figure 4: An example of a fictitious AI business startup assistant. Here, the AI presents the key factor in how the risk level was determined. When the user asks what would change if they manipulate that factor, the counterfactual statement is shown, confirming the impact of that specific factor in the model. (<a href='https://files.smashing.media/articles/beyond-black-box-practical-xai-ux-practitioners/4-ai-business-startup-assistant.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Ultimately, the <em>best</em> way to choose a technique is to let user research guide your decisions, ensuring that the explanations you design directly address actual user questions and concerns, rather than simply offering technical details for their own sake.</p>

<h2 id="xai-for-deep-reasoning-agents">XAI for Deep Reasoning Agents</h2>

<p>Some of the newest AI systems, known as <a href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/faqs-reasoning">deep reasoning agents</a>, produce an explicit “chain of thought” for every complex task. They do not merely cite sources; they show the logical, step-by-step path they took to arrive at a conclusion. While this transparency provides valuable context, a play-by-play that spans several paragraphs can feel overwhelming to a user simply trying to complete a task.</p>

<p>The principles of XAI, especially the Goldilocks Zone of Explanation, apply directly here. We can curate the journey, using progressive disclosure to show only the final conclusion and the most salient step in the thought process first. Users can then opt in to see the full, detailed, multi-step reasoning when they need to double-check the logic or find a specific fact. This approach respects user attention while preserving the agent’s full transparency.</p>

<h2 id="next-steps-empowering-your-xai-journey">Next Steps: Empowering Your XAI Journey</h2>

<p>Explainability is a fundamental pillar for building <strong>trustworthy and effective AI products</strong>. For the advanced practitioner looking to drive this change within their organization, the journey extends beyond design patterns into advocacy and continuous learning.</p>

<p>To deepen your understanding and practical application, consider exploring resources like the <a href="https://research.ibm.com/blog/ai-explainability-360">AI Explainability 360 (AIX360) toolkit</a> from IBM Research or Google’s <a href="https://pair-code.github.io/what-if-tool/">What-If Tool</a>, which offer interactive ways to explore model behavior and explanations. Engaging with communities like the <a href="https://responsibleaiforum.com">Responsible AI Forum</a> or specific research groups focused on human-centered AI can provide invaluable insights and collaboration opportunities.</p>

<p>Finally, be an advocate for XAI within your own organization. Frame explainability as a strategic investment. Consider a brief pitch to your leadership or cross-functional teams:</p>

<blockquote>“By investing in XAI, we’ll go beyond building trust; we’ll accelerate user adoption, reduce support costs by empowering users with understanding, and mitigate significant ethical and regulatory risks by exposing potential biases. This is good design and smart business.”</blockquote>

<p>Your voice, grounded in practical understanding, is crucial in bringing AI out of the black box and into a collaborative partnership with users.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Patrick Brosset</author><title>Masonry: Things You Won’t Need A Library For Anymore</title><link>https://www.smashingmagazine.com/2025/12/masonry-things-you-wont-need-library-anymore/</link><pubDate>Tue, 02 Dec 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/12/masonry-things-you-wont-need-library-anymore/</guid><description>CSS Masonry is almost here! Patrick Brosset takes a deep dive into what this long-awaited feature means for web developers and how you could make use of it in your own work.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/12/masonry-things-you-wont-need-library-anymore/" />
              <title>Masonry: Things You Won’t Need A Library For Anymore</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Masonry: Things You Won’t Need A Library For Anymore</h1>
                  
                    
                    <address>Patrick Brosset</address>
                  
                  <time datetime="2025-12-02T10:00:00&#43;00:00" class="op-published">2025-12-02T10:00:00+00:00</time>
                  <time datetime="2025-12-02T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>About 15 years ago, I was working at a company where we built apps for travel agents, airport workers, and airline companies. We also built our own in-house framework for UI components and single-page app capabilities.</p>

<p>We had components for everything: fields, buttons, tabs, ranges, datatables, menus, datepickers, selects, and multiselects. We even had a div component. Our div component was great by the way, it allowed us to do rounded corners on all browsers, which, believe it or not, wasn&rsquo;t an easy thing to do at the time.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="407"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png"
			
			sizes="100vw"
			alt="Div component, which allows to do rounded corners"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/1-div-component-example.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Our work took place at a point in our history when JS, Ajax, and dynamic HTML were seen as a revolution that brought us into the future. Suddenly, we could update a page dynamically, get data from a server, and avoid having to navigate to other pages, which was seen as slow and flashed a big white rectangle on the screen between the two pages.</p>

<p>There was a phrase, made popular by Jeff Atwood (the founder of StackOverflow), which read:</p>

<blockquote>“Any application that can be written in JavaScript will eventually be written in JavaScript.”<br /><br />&mdash; <a href='https://blog.codinghorror.com/all-programming-is-web-programming/#:~:text=any%20application%20that%C2%A0can%C2%A0be%20written%20in%20JavaScript%2C%C2%A0will%C2%A0eventually%20be%20written%20in%20JavaScript'>Jeff Atwood</a></blockquote>

<p>To us at the time, this felt like a dare to actually go and create those apps. It felt like a blanket approval to do everything with JS.</p>

<p>So we did everything with JS, and we didn’t really take the time to research other ways of doing things. We didn’t really feel the incentive to properly learn what HTML and CSS could do. We didn’t really perceive the web as an evolving app platform in its entirety. We mostly saw it as something we needed to work around, especially when it came to browser support. We could just throw more JS at it to get things done.</p>

<p>Would taking the time to learn more about how the web worked and what was available on the platform have helped me? Sure, I could probably have shaved a bunch of code that wasn’t truly needed. But, at the time, maybe not that much.</p>

<p>You see, browser differences were pretty significant back then. This was a time when Internet Explorer was still the dominant browser, with Firefox being the close second, but starting to lose market share due to Chrome rapidly gaining popularity. Although Chrome and Firefox were quite good at agreeing on web standards, the environments in which our apps were running meant that we had to support IE6 for a long time. Even when we were allowed to support IE8, we still had to deal with a lot of differences between browsers. Not only that, but the web of the time just didn&rsquo;t have that many capabilities built right into the platform.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://gs.statcounter.com/browser-market-share/all/worldwide/2010">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="492"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/2-browser-market-share.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/2-browser-market-share.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/2-browser-market-share.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/2-browser-market-share.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/2-browser-market-share.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/2-browser-market-share.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://gs.statcounter.com/browser-market-share/all/worldwide/2010'>statcounter</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/2-browser-market-share.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Fast forward to today. Things have changed tremendously. Not only do we have more of these capabilities than ever before, but the rate at which they become available has increased as well.</p>

<p>Let me ask the question again, then: Would taking the time to learn more about how the web works and what is available on the platform help you today? Absolutely yes. Learning to understand and use the web platform today puts you at a huge advantage over other developers.</p>

<p>Whether you work on performance, accessibility, responsiveness, all of them together, or just shipping UI features, if you want to do it as a responsible engineer, knowing the tools that are available to you helps you reach your goals faster and better.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="some-things-you-might-not-need-a-library-for-anymore">Some Things You Might Not Need A Library For Anymore</h2>

<p>Knowing what browsers support today, the question, then, is: What can we ditch? Do we need a div component to do rounded corners in 2025? Of course, we don’t. The <code>border-radius</code> property has been supported by all currently used browsers for more than 15 years at this point. And <code>corner-shape</code> is also coming soon, for even fancier corners.</p>

<p>Let’s take a look at relatively recent features that are now available in all major browsers, and which you can use to replace existing dependencies in your codebase.</p>

<p>The point isn&rsquo;t to immediately ditch all your beloved libraries and rewrite your codebase. As for everything else, you’ll need to take browser support into account first and decide based on other factors specific to your project. The following features are implemented in the three main browser engines (Chromium, WebKit, and Gecko), but you might have different browser support requirements that prevent you from using them right away. Now is still a good time to learn about these features, though, and perhaps plan to use them at some point.</p>

<h3 id="popovers-and-dialogs">Popovers And Dialogs</h3>

<p>The <a href="https://developer.mozilla.org/docs/Web/API/Popover_API">Popover API</a>, the <a href="https://developer.mozilla.org/docs/Web/HTML/Reference/Elements/dialog"><code>&lt;dialog&gt;</code> HTML element</a>, and the <a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Selectors/::backdrop"><code>::backdrop</code> pseudo-element</a> can help you get rid of dependencies on popup, tooltip, and dialog libraries, such as <a href="https://floating-ui.com/">Floating UI</a>, <a href="https://atomiks.github.io/tippyjs/">Tippy.js</a>, <a href="https://tetherjs.dev/docs/welcome/">Tether</a>, or <a href="https://react-tooltip.com/">React Tooltip</a>.</p>

<p>They handle accessibility and focus management for you, out of the box, are highly customizable by using CSS, and can easily be animated.</p>

<h3 id="accordions">Accordions</h3>

<p>The <a href="https://developer.mozilla.org/docs/Web/HTML/Reference/Elements/details"><code>&lt;details&gt;</code> element</a>, its <a href="https://developer.mozilla.org/docs/Web/HTML/Reference/Elements/details#name"><code>name</code> attribute</a> for mutually exclusive elements, and the <a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Selectors/::details-content"><code>::details-content</code></a> pseudo-element remove the need for accordion components like the <a href="https://getbootstrap.com/docs/5.3/components/accordion/">Bootstrap Accordion</a> or the <a href="https://mui.com/material-ui/react-accordion/">React Accordion component</a>.</p>

<p>Just using the platform here means it’s easier for folks who know HTML/CSS to understand your code without having to first learn to use a specific library. It also means you’re immune to breaking changes in the library or the discontinuation of that library. And, of course, it means less code to download and run. Mutually exclusive details elements don’t need JS to open, close, or animate.</p>

<h3 id="css-syntax">CSS Syntax</h3>

<p><a href="https://developer.mozilla.org/docs/Web/CSS/@layer">Cascade layers</a>, for a more organized CSS codebase, <a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Selectors/Nesting_selector">CSS nesting</a>, for more compact CSS, new color functions, <a href="https://developer.mozilla.org/docs/Web/CSS/CSS_colors/Relative_colors">relative colors</a>, and <a href="https://developer.mozilla.org/docs/Web/CSS/color_value/color-mix"><code>color-mix</code></a>, new Maths functions like <a href="https://developer.mozilla.org/docs/Web/CSS/abs"><code>abs()</code></a>, <a href="https://developer.mozilla.org/docs/Web/CSS/sign"><code>sign()</code></a>, <a href="https://developer.mozilla.org/docs/Web/CSS/pow"><code>pow()</code></a> and others help reduce dependencies on <a href="https://css-tricks.com/is-it-time-to-un-sass/">CSS pre-processors</a>, utility libraries like Bootstrap and Tailwind, or even runtime CSS-in-JS libraries.</p>

<p>The game changer <a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Selectors/:has"><code>:has()</code></a>, one of the most requested features for a long time, removes the need for more complicated JS-based solutions.</p>

<h3 id="js-utilities">JS Utilities</h3>

<p>Modern Array methods like <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast"><code>findLast()</code></a>, or <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/at"><code>at()</code></a>, as well as Set methods like <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/difference"><code>difference()</code></a>, <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/intersection"><code>intersection()</code></a>, <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/union"><code>union()</code></a> and others can reduce dependencies on libraries like <a href="https://lodash.com/">Lodash</a>.</p>

<h3 id="container-queries">Container Queries</h3>

<p><a href="https://developer.mozilla.org/docs/Web/CSS/CSS_containment/Container_queries">Container queries</a> make UI components respond to things other than the viewport size, and therefore make them more reusable across different contexts.</p>

<p>No need to use a JS-heavy UI library for this anymore, and no need to use a <a href="https://github.com/GoogleChromeLabs/container-query-polyfill">polyfill</a> either.</p>

<h3 id="layout">Layout</h3>

<p><a href="https://developer.mozilla.org/docs/Web/CSS/CSS_grid_layout">Grid</a>, <a href="https://developer.mozilla.org/docs/Web/CSS/CSS_grid_layout/Subgrid">subgrid</a>, <a href="https://developer.mozilla.org/docs/Learn_web_development/Core/CSS_layout/Flexbox">flexbox</a>, or <a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Properties/columns">multi-column</a> have been around for a long time now, but looking at the <a href="https://2025.stateofcss.com/en-US">results of the State of CSS surveys</a>, it’s clear that developers tend to be very cautious with adopting new things, and wait for a very long time before they do.</p>

<p>These features have been <a href="https://web-platform-dx.github.io/web-features/">Baseline</a> for a long time and you could use them to get rid of dependencies on things like the <a href="https://getbootstrap.com/docs/5.3/layout/grid/">Bootstrap’s grid system</a>, <a href="https://get.foundation/sites/docs/flexbox-utilities.html">Foundation Framework’s flexbox utilities</a>, <a href="https://bulma.io/documentation/grid/fixed-grid/">Bulma fixed grid</a>, <a href="https://materializecss.com/grid.html">Materialize grid</a>, or <a href="https://tailwindcss.com/docs/columns">Tailwind columns</a>.</p>

<p>I’m not saying you should drop your framework. Your team adopted it for a reason, and removing it might be a big project. But looking at what the web platform can offer without a third-party wrapper on top comes with a lot of benefits.</p>

<h2 id="things-you-might-not-need-anymore-in-the-near-future">Things You Might Not Need Anymore In The Near Future</h2>

<p>Now, let’s take a quick look at some of the things you will not need a library for in the near future. That is to say, the things below are not quite ready for mass adoption, but being aware of them and planning for potential later use can be helpful.</p>

<h3 id="anchor-positioning">Anchor Positioning</h3>

<p><a href="https://developer.mozilla.org/docs/Web/CSS/CSS_anchor_positioning">CSS anchor positioning</a> handles the positioning of popovers and tooltips relative to other elements, and takes care of keeping them in view, even when moving, scrolling, or resizing the page.</p>

<p>This is a great complement to the Popover API mentioned before, which will make it even easier to migrate away from more performance-intensive JS solutions.</p>

<h3 id="navigation-api">Navigation API</h3>

<p>The <a href="https://developer.mozilla.org/docs/Web/API/Navigation_API">Navigation API</a> can be used to handle navigation in single-page apps and might be a great complement, or even a replacement, to <a href="https://reactrouter.com/">React Router</a>, <a href="https://nextjs.org/docs/routing/introduction">Next.js routing</a>, or <a href="https://angular.io/guide/router">Angular routing tasks</a>.</p>

<h3 id="view-transitions-api">View Transitions API</h3>

<p>The <a href="https://developer.mozilla.org/docs/Web/API/View_Transition_API">View Transitions API</a> can animate between the different states of a page. On a single-page application, this makes smooth transitions between states very easy, and can help you get rid of animation libraries such as <a href="https://animejs.com/">Anime.js</a>, <a href="https://greensock.com/gsap/">GSAP</a>, or <a href="https://motion.dev/">Motion.dev</a>.</p>

<p>Even better, the API can also be used with multiple-page applications.</p>

<p>Remember earlier, when I said that the reason we built single-page apps at the company where I worked 15 years ago was to avoid the white flash of page reloads when navigating? Had that API been available at the time, we would have been able to achieve beautiful page transition effects without a single-page framework and without a huge initial download of the entire app.</p>

<h3 id="scroll-driven-animations">Scroll-driven Animations</h3>

<p><a href="https://developer.mozilla.org/docs/Web/CSS/CSS_scroll-driven_animations">Scroll-driven animations</a> run on the user’s scroll position, rather than over time, making them a great solution for storytelling and product tours.</p>

<p>Some people <a href="https://gt-era.com/">have gone a bit over the top</a> with it, but when used well, this can be a very effective design tool, and can help get rid of libraries like: <a href="https://scrollrevealjs.org/">ScrollReveal</a>, <a href="https://gsap.com/scroll/">GSAP Scroll</a>, or <a href="https://wowjs.uk/">WOW.js</a>.</p>

<h3 id="customizable-selects">Customizable Selects</h3>

<p>A <a href="https://developer.mozilla.org/docs/Learn_web_development/Extensions/Forms/Customizable_select">customizable select</a> is a normal <code>&lt;select&gt;</code> element that lets you fully customize its appearance and content, while ensuring accessibility and performance benefits.</p>

<p>This has been a long time coming, and a highly requested feature, and it’s amazing to see it come to the web platform soon. With a built-in customizable select, you can finally ditch all this hard-to-maintain JS code for your custom select components.</p>

<h3 id="css-masonry">CSS Masonry</h3>

<p><a href="https://developer.chrome.com/blog/masonry-update">CSS Masonry</a> is another upcoming web platform feature that I want to spend more time on.</p>

<p>With CSS Masonry, you can achieve layouts that are very hard, or even impossible, with flex, grid, or other built-in CSS layout primitives. Developers often resort to using third-party libraries to achieve Masonry layouts, such as the <a href="https://masonry.desandro.com/">Masonry JS library</a>.</p>

<p>But, more on that later. Let’s wrap this point up before moving on to Masonry.</p>

<div class="partners__lead-place"></div>

<h2 id="why-you-should-care">Why You Should Care</h2>

<p>The job market is full of web developers with experience in JavaScript and the latest frameworks of the day. So, really, what’s the point in learning to use the web platform primitives more, if you can do the same things with the libraries, utilities, and frameworks you already know today?</p>

<p>When an entire industry relies on these frameworks, and you can just pull in the right library, shouldn’t browser vendors just work with these libraries to make them load and run faster, rather than trying to convince developers to use the platform instead?</p>

<p>First of all, we do work with library authors, and we do make frameworks better by learning about what they use and improving those areas.</p>

<p>But secondly, “just using the platform” can bring pretty significant benefits.</p>

<h3 id="sending-less-code-to-devices">Sending Less Code To Devices</h3>

<p>The main benefit is that you end up sending far less code to your clients’ devices.</p>

<p>According to the <a href="https://almanac.httparchive.org/en/2024/">2024 Web Almanac</a>, the average number of HTTP requests is around 70 per site, <a href="https://almanac.httparchive.org/en/2024/javascript#how-many-javascript-requests-per-page">most of which is due to JavaScript with 23 requests</a>. In 2024, JS overtook images as the dominant file type too. The median number of page requests for JS files is 23, up 8% since 2022.</p>

<p>And page size continues to grow year over year. The median page weight is around 2MB now, which is 1.8MB more than it was 10 years ago.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://almanac.httparchive.org/en/2024/page-weight">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="462"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/3-median-page-weight.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/3-median-page-weight.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/3-median-page-weight.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/3-median-page-weight.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/3-median-page-weight.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/3-median-page-weight.png"
			
			sizes="100vw"
			alt="Median page weight"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://almanac.httparchive.org/en/2024/page-weight'>Web Almanac</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/3-median-page-weight.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Sure, your internet connection speed has probably increased, too, but that’s not the case for everyone. And not everyone has the same device capabilities either.</p>

<p>Pulling in third-party code for things you can do with the platform, instead, most probably means you ship more code, and therefore reach fewer customers than you normally would. On the web, bad loading performance leads to large abandonment rates and hurts brand reputation.</p>

<h3 id="running-less-code-on-devices">Running Less Code On Devices</h3>

<p>Furthermore, the code you do ship on your customers’ devices likely runs faster if it uses fewer JavaScript abstractions on top of the platform. It’s also probably more responsive and more accessible by default. All of this leads to more and happier customers.</p>

<p>Check my colleague Alex Russell’s <a href="https://infrequently.org/2024/01/performance-inequality-gap-2024/">yearly performance inequality gap blog</a>, which shows that premium devices are largely absent from markets with billions of users due to wealth inequality. And this gap is only growing over time.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://infrequently.org/2024/01/performance-inequality-gap-2024/#device-performance">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="452"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/4-device-performance.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/4-device-performance.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/4-device-performance.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/4-device-performance.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/4-device-performance.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/4-device-performance.png"
			
			sizes="100vw"
			alt="Device performance scores"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://infrequently.org/2024/01/performance-inequality-gap-2024/#device-performance'>Infrequently Noted</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/4-device-performance.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="built-in-masonry-layout">Built-in Masonry Layout</h2>

<p>One web platform feature that’s coming soon and which I’m very excited about is CSS Masonry.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="459"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png"
			
			sizes="100vw"
			alt="CSS Masonry"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/5-css-masonry.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let me start by explaining what Masonry is.</p>

<h3 id="what-is-masonry">What Is Masonry</h3>

<blockquote>Masonry is a type of layout that was made popular by Pinterest years ago. It creates independent tracks of content within which items pack themselves up as close to the start of the track as they can.</blockquote>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="604"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <a href='pinterest.com'>Pinterest</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/6-pinterest-portfolio.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Many people see Masonry as a great option for portfolios and photo galleries, which it certainly can do. But Masonry is <strong>more flexible</strong> than what you see on Pinterest, and it’s <strong>not limited to just waterfall-like layouts</strong>.</p>

<p>In a Masonry layout:</p>

<ul>
<li>Tracks can be columns or rows:</li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png"
			
			sizes="100vw"
			alt="Masonry layout with columns and rows"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/7-layout-columns-rows.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ul>
<li>Tracks of content don’t all have to be the same size:</li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="664"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png"
			
			sizes="100vw"
			alt="Masonry layout with tracks of different sizes"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/8-layout-different-sizes.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ul>
<li>Items can span multiple tracks:</li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="565"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png"
			
			sizes="100vw"
			alt="Masonry layout with multiple tracks"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/9-layout-multiple-tracks.png'>Large preview</a>)
    </figcaption>
  
</figure>

<ul>
<li>Items can be placed on specific tracks; they don’t have to always follow the automatic placement algorithm:</li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="628"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png"
			
			sizes="100vw"
			alt="Masonry layout with items on specific tracks"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/10-layout-items-specific-tracks.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h3 id="demos">Demos</h3>

<p>Here are a few simple demos I made by using the upcoming implementation of CSS Masonry in Chromium.</p>

<p><a href="https://microsoftedge.github.io/Demos/css-masonry/new-york.html">A photo gallery demo</a>, showing how items (the title in this case) can span multiple tracks:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="560"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png"
			
			sizes="100vw"
			alt="A photo gallery demo, showing items on multiple tracks"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/11-photo-gallery-different-sizes.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Another <a href="https://microsoftedge.github.io/Demos/css-masonry/nature.html">photo gallery showing tracks of different sizes</a>:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="437"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png"
			
			sizes="100vw"
			alt="A photo gallery showing tracks of different sizes"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/12-photo-gallery-different-tracks.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>A <a href="https://microsoftedge.github.io/Demos/css-masonry/the-daily-oddity.html">news site layout</a> with some tracks wider than others, and some items spanning the entire width of the layout:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="607"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png"
			
			sizes="100vw"
			alt="A news site layout with some tracks wider than others"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/13-news-site-layout.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>A <a href="https://microsoftedge.github.io/Demos/css-masonry/kanban.html">kanban board</a> showing that items can be placed onto specific tracks:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="320"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png"
			
			sizes="100vw"
			alt="A kanban board with items on specific tracks"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/14-kanban-board.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p><strong>Note</strong>: <em>The previous demos were made with a version of Chromium that’s not yet available to most web users, because CSS Masonry is only just starting to be implemented in browsers.</em></p>

<p>However, web developers have been happily using libraries to create Masonry layouts for years already.</p>

<h3 id="sites-using-masonry-today">Sites Using Masonry Today</h3>

<p>Indeed, Masonry is pretty common on the web today. Here are a few examples I found besides Pinterest:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="458"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png"
			
			sizes="100vw"
			alt="Erik Johansson&#39;s photography site"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://www.erikjo.com/work'>Erik Johansson</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/15-site-masonry.png'>Large preview</a>)
    </figcaption>
  
</figure>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="456"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png"
			
			sizes="100vw"
			alt="Kristian Hammerstad&#39;s site"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://www.kristianhammerstad.com/'>Kristian Hammerstad</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/16-masonry-site.png'>Large preview</a>)
    </figcaption>
  
</figure>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="479"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png"
			
			sizes="100vw"
			alt="L&#39;usine a Gouzou&#39;s catalogue"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://lusineagouzou.fr/catalogue'>L'usine a Gouzou</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/17-masonry-site.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>And a few more, less obvious, examples:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="428"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png"
			
			sizes="100vw"
			alt="Masonry layout from Agora"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      A row-direction Masonry layout from <a href='http://agora.io/en/'>www.agora.io</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/18-masonry-layout.png'>Large preview</a>)
    </figcaption>
  
</figure>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="633"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png"
			
			sizes="100vw"
			alt="Different size tracks from The Free Dictionary"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Different size tracks from <a href='https://www.thefreedictionary.com/'>www.thefreedictionary.com</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/19-different-size-tracks.png'>Large preview</a>)
    </figcaption>
  
</figure>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="605"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png"
			
			sizes="100vw"
			alt="Masonry layout of OneSignal"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://onesignal.com/'>OneSignal</a>. (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/20-masonry-layout.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>So, how were these layouts created?</p>

<h2 id="workarounds">Workarounds</h2>

<p>One trick that I’ve seen used is using a Flexbox layout instead, changing its direction to column, and setting it to wrap.</p>

<p>This way, you can place items of different heights in multiple, independent columns, giving the impression of a Masonry layout:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="578"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png"
			
			sizes="100vw"
			alt="Flexbox layout"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/21-flexbox-layout.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>There are, however, two limitations with this workaround:</p>

<ol>
<li>The order of items is different from what it would be with a real Masonry layout. With Flexbox, items fill the first column first and, when it’s full, then go to the next column. With Masonry, items would stack in whichever track (or column in this case) has more space available.</li>
<li>But also, and perhaps more importantly, this workaround requires that you set a fixed height to the Flexbox container; otherwise, no wrapping would occur.</li>
</ol>

<h2 id="third-party-masonry-libraries">Third-party Masonry Libraries</h2>

<p>For more advanced cases, developers have been using libraries.</p>

<p>The most well-known and popular library for this is simply called <a href="https://masonry.desandro.com/">Masonry</a>, and it gets downloaded about 200,000 times per week <a href="https://www.npmjs.com/package/masonry-layout">according to NPM</a>.</p>

<p>Squarespace also provides a <a href="https://www.beyondspace.studio/blog/squarespace-masonry-gallery-layout-guide#method-2-using-gallery-section">layout component that renders a Masonry layout</a>, for a no-code alternative, and many sites use it.</p>

<p>Both of these options use JavaScript code to place items in the layout.</p>

<div class="partners__lead-place"></div>

<h2 id="built-in-masonry">Built-in Masonry</h2>

<p>I’m really excited that Masonry is now starting to appear in browsers as a built-in CSS feature. Over time, you will be able to use Masonry just like you do Grid or Flexbox, that is, without needing any workarounds or third-party code.</p>

<p>My team at Microsoft has been implementing built-in Masonry support in the Chromium open source project, which Edge, Chrome, and many other browsers are based on. Mozilla was actually the first browser vendor to <a href="https://github.com/w3c/csswg-drafts/issues/4650">propose an experimental implementation of Masonry</a> back in 2020. And <a href="https://webkit.org/blog/15269/help-us-invent-masonry-layouts-for-css-grid-level-3/">Apple has also been very interested</a> in making this new web layout primitive happen.</p>

<p>The work to standardize the feature is also moving ahead, with agreement within the CSS working group about the general direction and even a new display type <a href="https://github.com/w3c/csswg-drafts/issues/12022#issuecomment-3525043825"><code>display: grid-lanes</code></a>.</p>

<p>If you want to learn more about Masonry and track progress, check out my <a href="https://patrickbrosset.com/lab/css-masonry-resources/">CSS Masonry resources</a> page.</p>

<p>In time, when Masonry becomes a Baseline feature, just like Grid or Flexbox, we’ll be able to simply use it and benefit from:</p>

<ul>
<li>Better performance,</li>
<li>Better responsiveness,</li>
<li>Ease of use and simpler code.</li>
</ul>

<p>Let’s take a closer look at these.</p>

<h3 id="better-performance">Better Performance</h3>

<p>Making your own Masonry-like layout system, or using a third-party library instead, means you’ll have to run JavaScript code to place items on the screen. This also means that this code will be <em>render blocking</em>. Indeed, either nothing will appear, or things won’t be in the right places or of the right sizes, until that JavaScript code has run.</p>

<p>Masonry layout is often used for the main part of a web page, which means the code would be making your main content appear later than it could otherwise have, degrading your <a href="https://web.dev/articles/lcp#what-is-lcp">LCP, or Largest Contentful Paint metric</a>, which plays a big role in perceived performance and search engine optimization.</p>

<p>I tested the Masonry JS library with a simple layout and by simulating a slow 4G connection in DevTools. The library is not very big (24KB, 7.8KB gzipped), but it took 600ms to load under my test conditions.</p>

<p>Here is a performance recording showing that long 600ms load time for the Masonry library, and that no other rendering activity happened while that was happening:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="541"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png"
			
			sizes="100vw"
			alt="A performance recording showing 600ms load time for the Masonry library"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/masonry-things-you-wont-need-library-anymore/22-performance-recording.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>In addition, after the initial load time, the downloaded script then needed to be parsed, compiled, and then run. All of which, as mentioned before, was blocking the rendering of the page.</p>

<p>With a built-in Masonry implementation in the browser, we won’t have a script to load and run. The browser engine will just do its thing during the initial page rendering step.</p>

<h3 id="better-responsiveness">Better Responsiveness</h3>

<p>Similar to when a page first loads, resizing the browser window leads to rendering the layout in that page again. At this point, though, if the page is using the Masonry JS library, there’s no need to load the script again, because it’s already here. However, the code that moves items in the right places needs to run.</p>

<p>Now this particular library seems to be pretty fast at doing this when the page loads. However, it animates the items when they need to move to a different place on window resize, and this makes a big difference.</p>

<p>Of course, users don’t spend time resizing their browser windows as much as we developers do. But this animated resizing experience can be pretty jarring and adds to the perceived time it takes for the page to adapt to its new size.</p>

<h3 id="ease-of-use-and-simpler-code">Ease Of Use And Simpler Code</h3>

<p>How easy it is to use a web feature and how simple the code looks are important factors that can make a big difference for your team. They can’t ever be as important as the final user experience, of course, but developer experience impacts maintainability. Using a built-in web feature comes with important benefits on that front:</p>

<ul>
<li>Developers who already know HTML, CSS, and JS will most likely be able to use that feature easily because it’s been designed to integrate well and be consistent with the rest of the web platform.</li>
<li>There’s no risk of breaking changes being introduced in how the feature is used.</li>
<li>There’s almost zero risk of that feature becoming deprecated or unmaintained.</li>
</ul>

<p>In the case of built-in Masonry, because it’s a layout primitive, you use it from CSS, just like Grid or Flexbox, no JS involved. Also, other layout-related CSS properties, such as gap, work as you’d expect them to. There are no tricks or workarounds to know about, and the things you do learn are documented on MDN.</p>

<p>For the Masonry JS lib, initialization is a bit complex: it requires a data attribute with a specific syntax, along with hidden HTML elements to set the column and gap sizes.</p>

<p>Plus, if you want to span columns, you need to include the gap size yourself to avoid problems:</p>

<div class="break-out">
<pre><code class="language-html">&lt;script src="https://unpkg.com/masonry-layout@4.2.2/dist/masonry.pkgd.min.js"&gt;&lt;/script&gt;
&lt;style&gt;
  .track-sizer,
  .item {
    width: 20%;
  }
  .gutter-sizer {
    width: 1rem;
  }
  .item {
    height: 100px;
    margin-block-end: 1rem;
  }
  .item:nth-child(odd) {
    height: 200px;
  }
  .item--width2 {
    width: calc(40% + 1rem);
  }
&lt;/style&gt;

&lt;div class="container"
  data-masonry='{ "itemSelector": ".item", "columnWidth": ".track-sizer", "percentPosition": true, "gutter": ".gutter-sizer" }'&gt;
  &lt;div class="track-sizer"&gt;&lt;/div&gt;
  &lt;div class="gutter-sizer"&gt;&lt;/div&gt;
  &lt;div class="item"&gt;&lt;/div&gt;
  &lt;div class="item item--width2"&gt;&lt;/div&gt;
  &lt;div class="item"&gt;&lt;/div&gt;
  ...
&lt;/div&gt;
</code></pre>
</div>

<p>Let’s compare this to what a built-in Masonry implementation would look like:</p>

<pre><code class="language-html">&lt;style&gt;
  .container {
    display: grid-lanes;
    grid-lanes: repeat(4, 20%);
    gap: 1rem;
  }
  .item {
    height: 100px;
  }
  .item:nth-child(odd) {
    height: 200px;
  }
  .item--width2 {
    grid-column: span 2;
  }
&lt;/style&gt;

&lt;div class="container"&gt;
  &lt;div class="item"&gt;&lt;/div&gt;
  &lt;div class="item item--width2"&gt;&lt;/div&gt;
  &lt;div class="item"&gt;&lt;/div&gt;
  ...
&lt;/div&gt;
</code></pre>

<p>Simpler, more compact code that can just use things like <code>gap</code> and where spanning tracks is done with <code>span 2</code>, just like in grid, and doesn’t require you to calculate the right width that includes the gap size.</p>

<h2 id="how-to-know-what-s-available-and-when-it-s-available">How To Know What’s Available And When It’s Available?</h2>

<p>Overall, the question isn’t really if you should use built-in Masonry over a JS library, but rather <em>when</em>. The Masonry JS library is amazing and has been filling a gap in the web platform for many years, and for many happy developers and users. It has a few drawbacks if you compare it to a built-in Masonry implementation, of course, but those are not important if that implementation isn’t ready.</p>

<p>It’s easy for me to list these cool new web platform features because I work at a browser vendor, and I therefore tend to know what’s coming. But developers often share, survey after survey, that keeping track of new things is hard. <strong>Staying informed is difficult</strong>, and companies don’t always prioritize learning anyway.</p>

<p>To help with this, here are a few resources that provide updates in simple and compact ways so you can get the information you need quickly:</p>

<ul>
<li><a href="https://web-platform-dx.github.io/web-features-explorer/">The Web platform features explorer site</a>:

<ul>
<li>You might be interested in its <a href="https://web-platform-dx.github.io/web-features-explorer/release-notes/october-2025/">release notes</a> page.</li>
<li>And, if you like RSS, check out <a href="https://web-platform-dx.github.io/web-features-explorer/release-notes.xml">the release notes feed</a>, as well as the Baseline <a href="https://web-platform-dx.github.io/web-features-explorer/newly-available.xml">Newly Available</a> and <a href="https://web-platform-dx.github.io/web-features-explorer/widely-available.xml">Widely Available</a> feeds.</li>
</ul></li>
<li><a href="https://webstatus.dev/">The Web Platform Status dashboard</a>:

<ul>
<li>You might like its various <a href="https://webstatus.dev/?q=baseline_date%3A2025-01-01..2025-12-31">Baseline year</a> pages.</li>
</ul></li>
<li><a href="https://chromestatus.com/roadmap">Chrome Platform Status’ roadmap page</a>.</li>
</ul>

<p>If you have a bit more time, you might also be interested in browser vendors’ release notes:</p>

<ul>
<li><a href="https://developer.chrome.com/release-notes">Chrome</a></li>
<li><a href="https://learn.microsoft.com/en-us/microsoft-edge/web-platform/release-notes/">Edge</a></li>
<li><a href="https://www.firefox.com/en-US/releases/">Firefox</a></li>
<li><a href="https://developer.apple.com/documentation/safari-release-notes">Safari</a></li>
</ul>

<p>For even more resources, check out my <a href="https://patrickbrosset.com/lab/navigating-the-web-platform/">Navigating the Web Platform Cheatsheet</a>.</p>

<h2 id="my-thing-is-still-not-implemented">My Thing Is Still Not Implemented</h2>

<p>That’s the other side of the problem. Even if you do find the time, energy, and ways to keep track, there’s still frustration with getting your voice heard and your favorite features implemented.</p>

<p>Maybe you’ve been waiting for years for a specific bug to be resolved, or a specific feature to ship in a browser where it’s still missing.</p>

<p>What I’ll say is <strong>browser vendors do listen</strong>. I’m part of several cross-organization teams where we discuss developer signals and feedback all the time. We look at many different sources of feedback, both internal at each browser vendor and external/public on forums, open source projects, blogs, and surveys. And, we’re always trying to create better ways for developers to share their specific needs and use cases.</p>

<p>So, if you can, please demand more from browser vendors and pressure us to implement the features you need. I get that it takes time, and can also be intimidating (not to mention a high barrier to entry), but it also works.</p>

<p>Here are a few ways you can get your (or your company’s) voice heard: Take the annual <a href="https://stateofjs.com/">State of JS</a>, <a href="https://stateofcss.com/">State of CSS</a>, and <a href="https://stateofhtml.com/">State of HTML</a> surveys. They play a big role in how browser vendors prioritize their work.</p>

<p>If you need a specific standard-based API to be implemented consistently across browsers, consider submitting a proposal at the next <a href="https://github.com/web-platform-tests/interop/">Interop project</a> iteration. It requires more time, but consider how <a href="https://docs.google.com/document/d/1ICqlNtdRXlhIlRuXFr1BRgy68R6Q5AwPv2b4hsIWUMY/edit">Shopify</a> and <a href="https://www.rumvision.com/blog/interop-2026-key-apis-for-sitespeed-and-rum/">RUMvision</a> shared their wish lists for Interop 2026. Detailed information like this can be very useful for browser vendors to prioritize.</p>

<p>For more useful links to influence browser vendors, check out my <a href="https://patrickbrosset.com/lab/navigating-the-web-platform/">Navigating the Web Platform Cheatsheet</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>To close, I hope this article has left you with a few things to think about:</p>

<ul>
<li>Excitement for Masonry and other upcoming web features.</li>
<li>A few web features you might want to start using.</li>
<li>A few pieces of custom or 3rd-party code you might be able to remove in favor of built-in features.</li>
<li>A few ways to keep track of what’s coming and influence browser vendors.</li>
</ul>

<p>More importantly, I hope I’ve convinced you of the benefits of using the web platform to its full potential.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Vitaly Friedman</author><title>Designing For Stress And Emergency</title><link>https://www.smashingmagazine.com/2025/11/designing-for-stress-emergency/</link><pubDate>Mon, 24 Nov 2025 13:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/11/designing-for-stress-emergency/</guid><description>Practical guidelines on designing time-critical products that prevent errors and improve accuracy. Part of the &lt;a href="https://measure-ux.com/">Measure UX &amp;amp; Design Impact&lt;/a> (use the code 🎟 &lt;code>IMPACT&lt;/code> to save 20% off today). With a &lt;a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">live UX training&lt;/a> starting next week.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/11/designing-for-stress-emergency/" />
              <title>Designing For Stress And Emergency</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Designing For Stress And Emergency</h1>
                  
                    
                    <address>Vitaly Friedman</address>
                  
                  <time datetime="2025-11-24T13:00:00&#43;00:00" class="op-published">2025-11-24T13:00:00+00:00</time>
                  <time datetime="2025-11-24T13:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>No design exists in isolation. As designers, we often imagine specific situations in which people will use our product. It might be indeed quite common &mdash; but there will also be other &mdash; <strong>urgent, frustrating, stressful situations</strong>. And they are the ones that we rarely account for.</p>

<p>So how do we account for such situations? How can we help people <strong>use our products while coping with stress</strong> &mdash; without adding to their cognitive load? Let’s take a closer look.</p>

<h2 id="study-where-your-product-fits-into-people-s-lives">Study Where Your Product Fits Into People’s Lives</h2>

<p>When designing digital products, sometimes we get a bit too attached to our <strong>shiny new features and flows</strong> &mdash; often forgetting the messy reality in which these features and flows have to neatly fit. And often it means 10s of other products, 100s of other tabs, and 1000s of other emails.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="600"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg"
			
			sizes="100vw"
			alt="An example of a split screen with two power consumption dashboards on a 22-inch screen."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Design never exists in isolation. It must fit the user’s context and their expectations to do its job. (Image source: <a href='https://seabits.com/engine-and-power-dashboards/'>Engine And Power Dashboard</a>) (<a href='https://files.smashing.media/articles/designing-for-stress-and-emergency/1-designing-for-stress-and-emergency.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>If your customers have to use a <strong>slightly older machine</strong>, with a <em>smallish</em> 22&rdquo; screen and a lot of background noise, they might use your product differently than you might have imagined, e.g., splitting the screen into halves to see both views at the same time (as displayed above).</p>

<p>Chances are high that our customers will use our product <strong>while doing something else</strong>, often with very little motivation, very little patience, plenty of urgent (and way more important) problems, and an unhealthy dose of stress. And that’s where our product must do its job well.</p>

<h2 id="what-is-stress">What Is Stress?</h2>

<p>What exactly do we mean when we talk about “stress”? As H Locke noted, stress is the <strong>body’s response to a situation it cannot handle</strong>. There is a mismatch between what people can control, their own skills, and the challenge in front of them.</p>

<p>If the situation seems unmanageable and the goal they want to achieve moves further away, it creates an enormous sense of <strong>failing</strong>. It can be extremely frustrating and demotivating.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://alypain.com/5-apps-to-reduce-stress-in-teens/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="804"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/2-designing-for-stress-and-emergency.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/designing-for-stress-and-emergency/2-designing-for-stress-and-emergency.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/designing-for-stress-and-emergency/2-designing-for-stress-and-emergency.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/designing-for-stress-and-emergency/2-designing-for-stress-and-emergency.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/designing-for-stress-and-emergency/2-designing-for-stress-and-emergency.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/2-designing-for-stress-and-emergency.jpg"
			
			sizes="100vw"
			alt="SOS Emergency System"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Stress has many levels. The key is not to let it spiral into dangerous zones. (Image source: <a href='https://alypain.com/5-apps-to-reduce-stress-in-teens/'>Alypain</a>) (<a href='https://files.smashing.media/articles/designing-for-stress-and-emergency/2-designing-for-stress-and-emergency.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Some failures have a local scope, but many have a <strong>far-reaching impact</strong>. Many people can’t choose the products they have to use for work, so when a tool fails repeatedly, causes frustration, or is unreliable, it affects the worker, the work, the colleagues, and processes within the organization. <strong>Fragility has a high cost</strong> &mdash; and so does frustration.</p>

<h2 id="how-stress-influences-user-interactions">How Stress Influences User Interactions</h2>

<p>It’s not a big surprise: stress disrupts attention, memory, cognition, and decision-making. It makes it difficult to prioritize and draw logical conclusions. In times of stress, we <strong>rely on fast, intuitive judgments</strong>, not reasoning. Typically, it leads to instinctive responses based on established habits.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="535"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png"
			
			sizes="100vw"
			alt="Designing For Stress And Emergency"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Overwhelming products can add to the cognitive load and lead to mistakes. However, people also get used to any products once they’ve used them long enough. (<a href='https://files.smashing.media/articles/designing-for-stress-and-emergency/3-designing-for-stress-and-emergency.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>When users are in an emergency, they experience <em>cognitive tunneling</em> — it&rsquo;s a state when their peripheral vision narrows, reading comprehension drops, fine motor skills deteriorate, and patience drops sharply. Under pressure, people often make decisions hastily, while others get entirely paralyzed. Either way is a likely <strong>path to mistakes</strong> &mdash; often irreversible ones and often without time for extensive deliberations.</p>

<p>Ideally, these decisions would be made way ahead of time &mdash; and then suggested when needed. But in practice, it’s not always possible. As it turns out, a good way to help people deal with stress is by <strong>providing order</strong> around how they manage it.</p>

<h2 id="single-tasking-instead-of-multi-tasking">Single-Tasking Instead Of Multi-Tasking</h2>

<p><a href="https://consensus.app/search/how-effective-are-people-at-multi-tasking-for-work/9GEx-KC0S8-OhSEgXClnrA/">People can’t <em>really</em> multi-task</a>, especially in very stressful situations or emergencies. Especially with a big chunk of work in front of them, people need some order to make progress, reliably. That’s why simpler pages usually work better than one big complex page.</p>

<p>Order means giving users a <strong>clear plan of action</strong> to complete a task. No distractions, no unnecessary navigation. We ask simple questions and <strong>prompt simple actions</strong>, one after another, one thing at a time.</p>














<figure class="
  
  
  ">
  
    <a href="https://designnotes.blog.gov.uk/2017/04/04/weve-published-the-task-list-pattern/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="607"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/4-designing-for-stress-and-emergency.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/designing-for-stress-and-emergency/4-designing-for-stress-and-emergency.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/designing-for-stress-and-emergency/4-designing-for-stress-and-emergency.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/designing-for-stress-and-emergency/4-designing-for-stress-and-emergency.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/designing-for-stress-and-emergency/4-designing-for-stress-and-emergency.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/4-designing-for-stress-and-emergency.png"
			
			sizes="100vw"
			alt="Task list pattern by Gov UK"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Poorly designed products can add to the cognitive load and lead to mistakes. (<a href='https://files.smashing.media/articles/designing-for-stress-and-emergency/4-designing-for-stress-and-emergency.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>An example of the plan is the <a href="https://designnotes.blog.gov.uk/2017/04/04/weve-published-the-task-list-pattern/">Task List Pattern</a>, invented by fine folks at Gov.uk. We break a task into a <strong>sequence of sub-tasks</strong>, describe them with actionable labels, assign statuses, and track progress.</p>

<p>To support accuracy, we revise <strong>default settings</strong>, values, presets, and actions. Also, the <strong>order of actions</strong> and buttons matters, so we put high-priority things first to make them easier to find. Then we add built-in safeguards (e.g., Undo feature) to prevent irreversible errors.</p>

<div class="partners__lead-place"></div>

<h2 id="supporting-in-emergencies">Supporting In Emergencies</h2>

<p>The most effective help during emergencies is to help people deal with the situation in a well-defined and effective way. That means being prepared for and designing an <strong>emergency mode</strong>, e.g., to activate instant alerts on emergency contacts, distribute pre-assigned tasks, and establish a line of communication.</p>














<figure class="
  
  
  ">
  
    <a href="https://www.redcross.org.au/emergencies/prepare/get-prepared-app/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="851"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/5-designing-for-stress-and-emergency.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/designing-for-stress-and-emergency/5-designing-for-stress-and-emergency.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/designing-for-stress-and-emergency/5-designing-for-stress-and-emergency.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/designing-for-stress-and-emergency/5-designing-for-stress-and-emergency.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/designing-for-stress-and-emergency/5-designing-for-stress-and-emergency.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/5-designing-for-stress-and-emergency.jpg"
			
			sizes="100vw"
			alt="Emergency plan by Rediplan App"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <a href='https://www.redcross.org.au/emergencies/prepare/get-prepared-app/'>Rediplan App</a> to prepare and act in case of emergencies. (<a href='https://files.smashing.media/articles/designing-for-stress-and-emergency/5-designing-for-stress-and-emergency.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p><a href="https://www.redcross.org.au/emergencies/prepare/get-prepared-app/">Rediplan App</a> by Australian Red Cross is an emergency plan companion that encourages citizens to <strong>prepare their documents and belongings</strong> with a few checklists and actions &mdash; including key contracts, meeting places, and medical information, all in one place.</p>

<h2 id="just-enough-friction">Just Enough Friction</h2>

<p>Not all stress is equally harmful, though. As <a href="https://www.kryshiggins.com/optimal-onboarding-zone/">Krystal Higgins points out</a>, if there is not enough friction when onboarding new users and the experience is <strong>too passive</strong> or users are hand-held even through the most basic tasks, you risk that they won’t realize the <strong>personal value</strong> they gain from the experience and, ultimately, lose interest.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://www.kryshiggins.com/optimal-onboarding-zone/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="459"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/6-designing-for-stress-and-emergency.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/designing-for-stress-and-emergency/6-designing-for-stress-and-emergency.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/designing-for-stress-and-emergency/6-designing-for-stress-and-emergency.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/designing-for-stress-and-emergency/6-designing-for-stress-and-emergency.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/designing-for-stress-and-emergency/6-designing-for-stress-and-emergency.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/designing-for-stress-and-emergency/6-designing-for-stress-and-emergency.png"
			
			sizes="100vw"
			alt="Bell Curve For Optimal User Onboarding"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      We need to find the sweet spot between value realization and friction to create experiences that keep users engaged. (Image source: <a href='https://www.kryshiggins.com/optimal-onboarding-zone/'>Krystal Higgins</a>) (<a href='https://files.smashing.media/articles/designing-for-stress-and-emergency/6-designing-for-stress-and-emergency.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="design-and-test-for-stress-cases">Design And Test For Stress Cases</h2>

<p><strong>Stress cases aren’t edge cases</strong>. We can’t predict the emotional state in which a user comes to our site or uses our product. A person looking for specific information on a hospital website or visiting a debt management website, for example, is most likely already stressed. Now, if the interface is overwhelming, it will only add to their cognitive load.</p>

<p>Stress-testing your product is critical to prevent this from happening. It’s useful to set up an annual day to <strong>stress test your product</strong> and refine emergency responses. It could be as simple as running <a href="https://contentdesign.intuit.com/foundations/content-testing/">content testing</a>, or running tests in a real, noisy, busy environment where users actually work — at peak times.</p>

<p>And in case of emergencies, we need to check if fallbacks work as expected and if the current UX of the product helps people manage failures and exceptional situations well enough.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>Emergencies <em>will</em> happen eventually &mdash; it’s just a matter of time. With good design, we can help <strong>mitigate risk and control damage</strong>, and make it hard to make irreversible mistakes. At its heart, that’s what good UX is exceptionally good at.</p>

<h2 id="key-takeaways">Key Takeaways</h2>

<p>People can’t multitask, especially in very stressful situations.</p>

<ul>
<li>Stress <strong>disrupts attention</strong>, memory, cognition, decision-making.</li>
<li>Also, it’s <strong>difficult to prioritize</strong> and draw logical conclusions.</li>
<li>Under stress, we rely on fast, intuitive judgments &mdash; not reasoning.</li>
<li>It leads to instinctive responses based on <strong>established habits</strong>.</li>
</ul>

<p>Goal: Design flows that support focus and high accuracy.</p>

<ul>
<li>Start with better default settings, values, presets, and actions.</li>
<li><strong>High-priority first</strong>: order of actions and buttons matters.</li>
<li>Break complex tasks down into a series of simple steps (10s–30s each).</li>
<li>Add built-in <strong>safeguards</strong> to prevent irreversible errors (Undo).</li>
</ul>

<p>Shift users to single-tasking: ask for one thing at a time.</p>

<ul>
<li><strong>Simpler pages</strong> might work better than one complex page.</li>
<li>Suggest a <strong>step-by-step plan of action</strong> to follow along.</li>
<li>Consider, design, and test flows for emergency responses ahead of time.</li>
<li>Add emergency mode for <strong>instant alerts</strong> and task assignments.</li>
</ul>

<h2 id="meet-how-to-measure-ux-and-design-impact">Meet “How To Measure UX And Design Impact”</h2>

<p>You can find more details on <strong>UX Strategy</strong> in 🪴&nbsp;<a href="https://measure-ux.com/"><strong>Measure UX &amp; Design Impact</strong></a> (8h), a practical guide for designers and UX leads to measure and show your UX impact on business. Use the code 🎟 <code>IMPACT</code> to save 20% off today. <a href="https://measure-ux.com/">Jump to the details</a>.</p>

<figure style="margin-bottom:0;padding-bottom:0" class="article__image">
    <a href="https://measure-ux.com/" title="How To Measure UX and Design Impact, with Vitaly Friedman">
    <img width="900" height="466" style="border-radius: 11px" src="https://files.smashing.media/articles/ux-metrics-video-course-release/measure-ux-and-design-impact-course.png" alt="How to Measure UX and Design Impact, with Vitaly Friedman.">
    </a>
</figure>

<div class="book-cta__inverted"><div class="book-cta" data-handler="ContentTabs" data-mq="(max-width: 480px)"><nav class="content-tabs content-tabs--books"><ul><li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">
Video + UX Training</button></a></li><li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">Video only</button></a></li></ul></nav><div class="book-cta__col book-cta__hardcover content-tab--content"><h3 class="book-cta__title"><span>Video + UX Training</span></h3><span class="book-cta__price"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>495<span class="sup">.00</span></span></span> <span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>799<span class="sup">.00</span></span></span></span></span>
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3951439" class="btn btn--full btn--medium btn--text-shadow">
Get Video + UX Training<div></div></a><p class="book-cta__desc">25 video lessons (8h) + <a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">Live UX Training</a>.<br>100 days money-back-guarantee.</p></div><div class="book-cta__col book-cta__ebook content-tab--content"><h3 class="book-cta__title"><span>Video only</span></h3><div data-audience="anonymous free supporter" data-remove="true"><span class="book-cta__price" data-handler="PriceTag"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>250<span class="sup">.00</span></span></span><span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>395<span class="sup">.00</span></span></span></span></div>
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3950630" class="btn btn--full btn--medium btn--text-shadow">
Get the video course<div></div></a><p class="book-cta__desc" data-audience="anonymous free supporter" data-remove="true">25 video lessons (8h). Updated yearly.<br>Also available as a <a href="https://smart-interface-design-patterns.thinkific.com/enroll/3082557?price_id=3951421">UX Bundle with 2 video courses.</a></p></div><span></span></div></div>

<h2 id="useful-resources">Useful Resources</h2>

<ul>
<li>“<a href="https://medium.com/design-bootcamp/ux-case-study-standby-17000867133c">Designing The SOS Emergency System</a>”, by Ritik Jayy</li>
<li>“<a href="https://medium.com/net-magazine/designing-for-crisis-9cab10b4c519">Designing For Crisis</a>”, by Eric Meyer</li>
<li>“<a href="https://medium.com/designing-services/designing-for-stressed-out-users-part-1-4489793dbe41">Designing For Stressed Out Users</a>” (Series), by H Locke</li>
<li><a href="https://uxpodcast.com/293-life-death-design-katie-swindler/">Designing For Stress</a> (Podcast), by Katie Swindler</li>
<li><a href="https://www.linkedin.com/posts/vitalyfriedman_ux-design-activity-7167433494200066048-trWE">Designing For Edge Cases and Exceptions</a>, by yours truly</li>
<li><a href="https://dfrlbook.com/"><em>Design For Real Life</em></a>, by Sara Wachter-Boettcher, Eric Mayer</li>
<li>“<a href="https://www.kryshiggins.com/optimal-onboarding-zone/">Optimal Stress Levels For Onboarding</a>, by Krystal Higgins</li>
</ul>

<h3 id="further-reading">Further Reading</h3>

<ul>
<li>“<a href="https://www.smashingmagazine.com/2025/09/how-minimize-environmental-impact-website/">How To Minimize The Environmental Impact Of Your Website</a>”, James Chudley</li>
<li>“<a href="https://www.smashingmagazine.com/2025/10/ai-ux-achieve-more-with-less/">AI In UX: Achieve More With Less</a>”, Paul Boag</li>
<li>“<a href="https://www.smashingmagazine.com/2025/10/how-make-ux-research-hard-to-ignore/">How To Make Your UX Research Hard To Ignore</a>”, Vitaly Friedman</li>
<li>“<a href="https://www.smashingmagazine.com/2025/09/from-prompt-to-partner-designing-custom-ai-assistant/">From Prompt To Partner: Designing Your Custom AI Assistant</a>,” Lyndon Cerejo</li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Andy Clarke</author><title>Smashing Animations Part 6: Magnificent SVGs With `&lt;use>` And CSS Custom Properties</title><link>https://www.smashingmagazine.com/2025/11/smashing-animations-part-6-svgs-css-custom-properties/</link><pubDate>Fri, 07 Nov 2025 15:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/11/smashing-animations-part-6-svgs-css-custom-properties/</guid><description>SVG is one of those web technologies that’s both elegant and, at times, infuriating. In this article, pioneering author and web designer &lt;a href="https://stuffandnonsense.co.uk">Andy Clarke&lt;/a> explains his technique for animating SVG elements that are hidden in the Shadow DOM.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/11/smashing-animations-part-6-svgs-css-custom-properties/" />
              <title>Smashing Animations Part 6: Magnificent SVGs With `&lt;use&gt;` And CSS Custom Properties</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Smashing Animations Part 6: Magnificent SVGs With `&lt;use&gt;` And CSS Custom Properties</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-11-07T15:00:00&#43;00:00" class="op-published">2025-11-07T15:00:00+00:00</time>
                  <time datetime="2025-11-07T15:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>I explained recently how I use <code>&lt;symbol&gt;</code>, <code>&lt;use&gt;</code>, and CSS Media Queries to develop what I call <a href="https://www.smashingmagazine.com/2025/10/smashing-animations-part-5-building-adaptive-svgs/">adaptive SVGs</a>. Symbols let us define an element once and then <em>use</em> it again and again, making SVG animations easier to maintain, more efficient, and lightweight.</p>

<p>Since I wrote that explanation, I’ve designed and implemented new <a href="https://stuffandnonsense.co.uk/blog/say-hello-to-my-magnificent-7">Magnificent 7</a> animated graphics across <a href="https://stuffandnonsense.co.uk/">my website</a>. They play on the web design pioneer theme, featuring seven magnificent Old West characters.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://stuffandnonsense.co.uk/">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/1-graphics-old-west-characters.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/1-graphics-old-west-characters.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/1-graphics-old-west-characters.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/1-graphics-old-west-characters.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/1-graphics-old-west-characters.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/1-graphics-old-west-characters.png"
			
			sizes="100vw"
			alt="Graphics featuring seven magnificent Old West characters"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      View this animated SVG on <a href='https://stuffandnonsense.co.uk/'>my website</a>. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/1-graphics-old-west-characters.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p><code>&lt;symbol&gt;</code> and <code>&lt;use&gt;</code> let me define a character design and reuse it across multiple SVGs and pages. First, I created my characters and put each into a <code>&lt;symbol&gt;</code> inside a hidden library SVG:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;!-- Symbols library --&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" style="display:none;"&gt;
 &lt;symbol id="outlaw-1"&gt;[...]&lt;/symbol&gt;
 &lt;symbol id="outlaw-2"&gt;[...]&lt;/symbol&gt;
 &lt;symbol id="outlaw-3"&gt;[...]&lt;/symbol&gt;
 &lt;!-- etc. --&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>Then, I referenced those symbols in two other SVGs, one for large and the other for small screens:</p>

<pre><code class="language-svg">&lt;!-- Large screens --&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" id="svg-large"&gt;
 &lt;use href="outlaw-1" /&gt;
 &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;!-- Small screens --&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" id="svg-small"&gt;
 &lt;use href="outlaw-1" /&gt;
 &lt;!-- ... --&gt;
&lt;/svg&gt;
</code></pre>

<p>Elegant. But then came the infuriating. I could reuse the characters, but couldn’t animate or style them. I added CSS rules targeting elements within the symbols referenced by a <code>&lt;use&gt;</code>, but nothing happened. Colours stayed the same, and things that should move stayed static. It felt like I’d run into an invisible barrier, and I had.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="understanding-the-shadow-dom-barrier">Understanding The Shadow DOM Barrier</h2>

<p>When you reference the contents of a <code>symbol</code> with <code>use</code>, a browser creates a copy of it in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM">Shadow DOM</a>. Each <code>&lt;use&gt;</code> instance becomes its own encapsulated copy of the referenced <code>&lt;symbol&gt;</code>, meaning that CSS from outside can’t break through the barrier to style any elements directly. For example, in normal circumstances, this <code>tapping</code> value triggers a CSS animation:</p>

<pre><code class="language-svg">&lt;g class="outlaw-1-foot tapping"&gt;
 &lt;!-- ... --&gt;
&lt;/g&gt;
</code></pre>

<pre><code class="language-css">.tapping {
  animation: tapping 1s ease-in-out infinite;
}
</code></pre>

<p>But when the same animation is applied to a <code>&lt;use&gt;</code> instance of that same foot, nothing happens:</p>

<pre><code class="language-svg">&lt;symbol id="outlaw-1"&gt;
 &lt;g class="outlaw-1-foot"&gt;&lt;!-- ... --&gt;&lt;/g&gt;
&lt;/symbol&gt;

&lt;use href="#outlaw-1" class="tapping" /&gt;
</code></pre>

<pre><code class="language-css">.tapping {
  animation: tapping 1s ease-in-out infinite;
}
</code></pre>

<p>That’s because the <code>&lt;g&gt;</code> inside the <code>&lt;symbol&gt;</code> element is in a protected shadow tree, and the CSS Cascade stops dead at the <code>&lt;use&gt;</code> boundary. This behaviour can be frustrating, but it’s intentional as it ensures that reused symbol content stays consistent and predictable.</p>

<p>While learning how to develop adaptive SVGs, I found all kinds of attempts to work around this behaviour, but most of them sacrificed the reusability that makes SVG so elegant. I didn’t want to duplicate my characters just to make them blink at different times. I wanted a single <code>&lt;symbol&gt;</code> with instances that have their own timings and expressions.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png"
			
			sizes="100vw"
			alt="Several animated elements within a single SVG symbol"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Several animated elements within a single SVG symbol. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/2-animated-elements-single-svg-symbol.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="css-custom-properties-to-the-rescue">CSS Custom Properties To The Rescue</h2>

<p>While working on my pioneer animations, I learned that <strong>regular CSS values can’t cross the boundary into the Shadow DOM, but CSS Custom Properties can</strong>. And even though you can’t directly style elements inside a <code>&lt;symbol&gt;</code>, you can pass custom property values to them. So, when you insert custom properties into an inline style, a browser looks at the cascade, and those styles become available to elements inside the <code>&lt;symbol&gt;</code> being referenced.</p>

<p>I added <code>rotate</code> to an inline style applied to the <code>&lt;symbol&gt;</code> content:</p>

<pre><code class="language-svg">&lt;symbol id="outlaw-1"&gt;
  &lt;g class="outlaw-1-foot" style="
    transform-origin: bottom right; 
    transform-box: fill-box; 
    transform: rotate(var(--foot-rotate));"&gt;
    &lt;!-- ... --&gt;
  &lt;/g&gt;
&lt;/symbol&gt;
</code></pre>

<p>Then, defined the foot tapping animation and applied it to the element:</p>

<pre><code class="language-css">@keyframes tapping {
  0%, 60%, 100% { --foot-rotate: 0deg; }
  20% { --foot-rotate: -5deg; }
  40% { --foot-rotate: 2deg; }
}

use[data-outlaw="1"] {
  --foot-rotate: 0deg;
  animation: tapping 1s ease-in-out infinite;
}
</code></pre>

<h2 id="passing-multiple-values-to-a-symbol">Passing Multiple Values To A Symbol</h2>

<p>Once I’ve set up a symbol to use CSS Custom Properties, I can pass as many values as I want to any <code>&lt;use&gt;</code> instance. For example, I might define variables for <code>fill</code>, <code>opacity</code>, or <code>transform</code>. What’s elegant is that each <code>&lt;symbol&gt;</code> instance can then have its own set of values.</p>

<pre><code class="language-svg">&lt;g class="eyelids" style="
  fill: var(--eyelids-colour, #f7bea1);
  opacity: var(--eyelids-opacity, 1);
  transform: var(--eyelids-scale, 0);"
&gt;
  &lt;!-- etc. --&gt;
&lt;/g&gt;
</code></pre>

<pre><code class="language-css">use[data-outlaw="1"] {
  --eyelids-colour: #f7bea1; 
  --eyelids-opacity: 1;
}

use[data-outlaw="2"] {
  --eyelids-colour: #ba7e5e; 
  --eyelids-opacity: 0;
}
</code></pre>

<p>Support for passing CSS Custom Properties like this is solid, and every contemporary browser handles this behaviour correctly. Let me show you a few ways I’ve been using this technique, starting with a multi-coloured icon system.</p>

<div class="partners__lead-place"></div>

<h2 id="a-multi-coloured-icon-system">A Multi-Coloured Icon System</h2>

<p>When I need to maintain a set of icons, I can define an icon once inside a <code>&lt;symbol&gt;</code> and then use custom properties to apply colours and effects. Instead of needing to duplicate SVGs for every theme, each <code>use</code> can carry its own values.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="167"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png"
			
			sizes="100vw"
			alt="Custom properties for the fill colours in several Bluesky icons"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Custom properties for the fill colours in several Bluesky icons. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/3-custom-properties-colours.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>For example, I applied an <code>--icon-fill</code> custom property for the default <code>fill</code> colour of the <code>&lt;path&gt;</code> in this Bluesky icon :</p>

<pre><code class="language-svg">&lt;symbol id="icon-bluesky"&gt;
  &lt;path fill="var(--icon-fill, currentColor)" d="..." /&gt;
&lt;/symbol&gt;
</code></pre>

<p>Then, whenever I need to vary how that icon looks &mdash; for example, in a <code>&lt;header&gt;</code> and <code>&lt;footer&gt;</code> &mdash; I can pass new <code>fill</code> colour values to each instance:</p>

<pre><code class="language-html">&lt;header&gt;
  &lt;svg xmlns="http://www.w3.org/2000/svg"&gt;
    &lt;use href="#icon-bluesky" style="--icon-fill: #2d373b;" /&gt;
  &lt;/svg&gt;
&lt;/header&gt;

&lt;footer&gt;
  &lt;svg xmlns="http://www.w3.org/2000/svg"&gt;
    &lt;use href="#icon-bluesky" style="--icon-fill: #590d1a;" /&gt;
  &lt;/svg&gt;
&lt;/footer&gt;
</code></pre>

<p>These icons are the same shape but look different thanks to their inline styles.</p>

<h2 id="data-visualisations-with-css-custom-properties">Data Visualisations With CSS Custom Properties</h2>

<p>We can use <code>&lt;symbol&gt;</code> and <code>&lt;use&gt;</code> in plenty more practical ways. They’re also helpful for creating lightweight data visualisations, so imagine an infographic about three famous <a href="https://en.wikipedia.org/wiki/American_frontier">Wild West</a> sheriffs: <a href="https://en.wikipedia.org/wiki/Wyatt_Earp">Wyatt Earp</a>, <a href="https://en.wikipedia.org/wiki/Pat_Garrett">Pat Garrett</a>, and <a href="https://en.wikipedia.org/wiki/Bat_Masterson">Bat Masterson</a>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="421"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png"
			
			sizes="100vw"
			alt="Data visualisations with CSS Custom Properties"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Data visualisations with CSS Custom Properties. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/4-data-visualisations-css-custom-properties.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Each sheriff’s profile uses the same set of SVG three symbols: one for a bar representing the length of a sheriff’s career, another to represent the number of arrests made, and one more for the number of kills. Passing custom property values to each <code>&lt;use&gt;</code> instance can vary the bar lengths, arrests scale, and kills colour without duplicating SVGs. I first created symbols for those items:</p>

<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" style="display:none;"&gt;
  &lt;symbol id="career-bar"&gt;
    &lt;rect
      height="10"
      width="var(--career-length, 100)" 
      fill="var(--career-colour, #f7bea1)"
    /&gt;
  &lt;/symbol&gt;
  
  &lt;symbol id="arrests-badge"&gt;
    &lt;path 
      fill="var(--arrest-color, #d0985f)" 
      transform="scale(var(--arrest-scale, 1))"
    /&gt;
  &lt;/symbol&gt;
  
  &lt;symbol id="kills-icon"&gt;
    &lt;path fill="var(--kill-colour, #769099)" /&gt;
  &lt;/symbol&gt;
&lt;/svg&gt;
</code></pre>

<p>Each symbol accepts one or more values:</p>

<ul>
<li><strong><code>--career-length</code></strong> adjusts the <code>width</code> of the career bar.</li>
<li><strong><code>--career-colour</code></strong> changes the <code>fill</code> colour of that bar.</li>
<li><strong><code>--arrest-scale</code></strong> controls the arrest badge size.</li>
<li><strong><code>--kill-colour</code></strong> defines the <code>fill</code> colour of the kill icon.</li>
</ul>

<p>I can use these to develop a profile of each sheriff using <code>&lt;use&gt;</code> elements with different inline styles, starting with Wyatt Earp.</p>

<div class="break-out">
<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg"&gt;
  &lt;g id="wyatt-earp"&gt;
    &lt;use href="&#35;career-bar" style="--career-length: 400; --career-color: &#35;769099;"/&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 2;" /&gt;
    &lt;!-- ... --&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 2;" /&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 1;" /&gt;
    &lt;use href="&#35;kills-icon" style="--kill-color: &#35;769099;" /&gt;
  &lt;/g&gt;

  &lt;g id="pat-garrett"&gt;
    &lt;use href="&#35;career-bar" style="--career-length: 300; --career-color: &#35;f7bea1;"/&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 2;" /&gt;
    &lt;!-- ... --&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 2;" /&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 1;" /&gt;
    &lt;use href="&#35;kills-icon" style="--kill-color: &#35;f7bea1;" /&gt;
  &lt;/g&gt;

  &lt;g id="bat-masterson"&gt;
    &lt;use href="#career-bar" style="--career-length: 200; --career-color: &#35;c2d1d6;"/&gt;
    &lt;use href="#arrests-badge" style="--arrest-scale: 2;" /&gt;
    &lt;!-- ... --&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 2;" /&gt;
    &lt;use href="&#35;arrests-badge" style="--arrest-scale: 1;" /&gt;
    &lt;use href="&#35;kills-icon" style="--kill-color: &#35;c2d1d6;" /&gt;
  &lt;/g&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>Each <code>&lt;use&gt;</code> shares the same symbol elements, but the inline variables change their colours and sizes. I can even animate those values to highlight their differences:</p>

<pre><code class="language-css">@keyframes pulse {
  0%, 100% { --arrest-scale: 1; }
  50% { --arrest-scale: 1.2; }
}

use[href="#arrests-badge"]:hover {
  animation: pulse 1s ease-in-out infinite;
}
</code></pre>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aCSS%20Custom%20Properties%20aren%e2%80%99t%20only%20helpful%20for%20styling;%20they%20can%20also%20channel%20data%20between%20HTML%20and%20SVG%e2%80%99s%20inner%20geometry,%20binding%20visual%20attributes%20like%20colour,%20length,%20and%20scale%20to%20semantics%20like%20arrest%20numbers,%20career%20length,%20and%20kills.%0a&url=https://smashingmagazine.com%2f2025%2f11%2fsmashing-animations-part-6-svgs-css-custom-properties%2f">
      
CSS Custom Properties aren’t only helpful for styling; they can also channel data between HTML and SVG’s inner geometry, binding visual attributes like colour, length, and scale to semantics like arrest numbers, career length, and kills.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<h2 id="ambient-animations">Ambient Animations</h2>

<p>I started learning to animate elements within symbols while creating the animated graphics for my website’s Magnificent 7. To reduce complexity and make my code lighter and more maintainable, I needed to define each character once and reuse it across SVGs:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;!-- Symbols library --&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" style="display:none;"&gt;
  &lt;symbol id="outlaw-1"&gt;[…]&lt;/symbol&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;!-- Large screens --&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" id="svg-large"&gt;
  &lt;use href="outlaw-1" /&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;!-- Small screens --&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" id="svg-small"&gt;
  &lt;use href="outlaw-1" /&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;
</code></pre>
</div>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png"
			
			sizes="100vw"
			alt="My website’s Magnificent 7 characters"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      My website’s Magnificent 7 characters. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/5-characters.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But I didn’t want those characters to stay static; I needed subtle movements that would bring them to life. I wanted their eyes to blink, their feet to tap, and their moustache whiskers to twitch. So, to animate these details, I pass animation data to elements inside those symbols using CSS Custom Properties, starting with the blinking.</p>

<p>I implemented the blinking effect by placing an SVG group over the outlaws’ eyes and then changing its <code>opacity</code>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="333"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png"
			
			sizes="100vw"
			alt="Blinking effect by animating eyelids’ opacity."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Blinking effect by animating eyelids’ opacity. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/6-blinking-effect.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>To make this possible, I added an inline style with a CSS Custom Property to the group:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;symbol id="outlaw-1" viewBox="0 0 712 2552"&gt;
 &lt;g class="eyelids" style="opacity: var(--eyelids-opacity, 1);"&gt;
    &lt;!-- ... --&gt;
  &lt;/g&gt;
&lt;/symbol&gt;
</code></pre>
</div>

<p>Then, I defined the blinking animation by changing <code>--eyelids-opacity</code>:</p>

<pre><code class="language-css">@keyframes blink {
  0%, 92% { --eyelids-opacity: 0; }
  93%, 94% { --eyelids-opacity: 1; }
  95%, 97% { --eyelids-opacity: 0.1; }
  98%, 100% { --eyelids-opacity: 0; }
}
</code></pre>

<p>…and applied it to every character:</p>

<div class="break-out">
<pre><code class="language-css">use[data-outlaw] {
  --blink-duration: 4s;
  --eyelids-opacity: 1;
  animation: blink var(--blink-duration) infinite var(--blink-delay);
}
</code></pre>
</div>

<p>…so that each character wouldn’t blink at the same time, I set a different <code>--blink-delay</code> before they all start blinking, by passing another Custom Property:</p>

<pre><code class="language-css">use[data-outlaw="1"] { --blink-delay: 1s; }
use[data-outlaw="2"] { --blink-delay: 2s; }
<!-- ... -->
use[data-outlaw="7"] { --blink-delay: 3s; }
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png"
			
			sizes="100vw"
			alt="Foot tapping effect by animating the foot’s rotation"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Foot tapping effect by animating the foot’s rotation. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/7-foot-tapping-effect.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Some of the characters tap their feet, so I added an inline style with a CSS Custom Property to those groups, too:</p>

<pre><code class="language-svg">&lt;symbol id="outlaw-1" viewBox="0 0 712 2552"&gt;
  &lt;g class="outlaw-1-foot" style="
    transform-origin: bottom right; 
    transform-box: fill-box; 
    transform: rotate(var(--foot-rotate));"&gt;
  &lt;/g&gt;
&lt;/symbol&gt;
</code></pre>

<p>Defining the foot-tapping animation:</p>

<pre><code class="language-css">@keyframes tapping {
  0%, 60%, 100% { --foot-rotate: 0deg; }
  20% { --foot-rotate: -5deg; }
  40% { --foot-rotate: 2deg; }
}
</code></pre>

<p>And adding those extra Custom Properties to the characters’ declaration:</p>

<pre><code class="language-css">use[data-outlaw] {
  --blink-duration: 4s;
  --eyelids-opacity: 1;
  --foot-rotate: 0deg;
  animation: 
    blink var(--blink-duration) infinite var(--blink-delay),
    tapping 1s ease-in-out infinite;
}
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="333"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png"
			
			sizes="100vw"
			alt="Jiggling effect by animating the moustaches’ translation"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Jiggling effect by animating the moustaches’ translation. (<a href='https://files.smashing.media/articles/smashing-animations-part-6-svgs-css-custom-properties/8-jiggling-effect.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>…before finally making the character’s whiskers jiggle via an inline style with a CSS Custom Property which describes how his moustache transforms:</p>

<pre><code class="language-svg">&lt;symbol id="outlaw-1" viewBox="0 0 712 2552"&gt;
  &lt;g class="outlaw-1-tashe" style="
    transform: translateX(var(--jiggle-x, 0px));"
  &gt;
    &lt;!-- ... --&gt;
  &lt;/g&gt;
&lt;/symbol&gt;
</code></pre>

<p>Defining the jiggle animation:</p>

<pre><code class="language-css">@keyframes jiggle {
  0%, 100% { --jiggle-x: 0px; }
  20% { --jiggle-x: -3px; }
  40% { --jiggle-x: 2px; }
  60% { --jiggle-x: -1px; }
  80% { --jiggle-x: 4px; }
}
</code></pre>

<p>And adding those properties to the characters’ declaration:</p>

<pre><code class="language-css">use[data-outlaw] {
  --blink-duration: 4s;
  --eyelids-opacity: 1;
  --foot-rotate: 0deg;
  --jiggle-x: 0px;
  animation: 
    blink var(--blink-duration) infinite var(--blink-delay),
    jiggle 1s ease-in-out infinite,
    tapping 1s ease-in-out infinite;
}
</code></pre>

<p>With these moving parts, the characters come to life, but my markup remains remarkably lean. By combining several animations into a single declaration, I can choreograph their movements without adding more elements to my SVG. Every outlaw shares the same base <code>&lt;symbol&gt;</code>, and their individuality comes entirely from CSS Custom Properties.</p>

<div class="partners__lead-place"></div>

<h2 id="pitfalls-and-solutions">Pitfalls And Solutions</h2>

<p>Even though this technique might seem bulletproof, there are a few traps it’s best to avoid:</p>

<ul>
<li><strong>CSS Custom Properties only work if they’re referenced with a <code>var()</code> inside a <code>&lt;symbol&gt;</code>.</strong> Forget that, and you’ll wonder why nothing updates. Also, properties that aren’t naturally inherited, like <code>fill</code> or <code>transform</code>, need to use <code>var()</code> in their value to benefit from the cascade.</li>
<li><strong>It’s always best to include a fallback value alongside a custom property</strong>, like <code>opacity: var(--eyelids-opacity, 1);</code> to ensure SVG elements render correctly even without custom property values applied.</li>
<li><strong>Inline styles set via the <code>style</code> attribute take precedence</strong>, so if you mix inline and external CSS, remember that Custom Properties follow normal cascade rules.</li>
<li><strong>You can always use DevTools to inspect custom property values.</strong> Select a <code>&lt;use&gt;</code> instance and check the Computed Styles panel to see which custom properties are active.</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>The <code>&lt;symbol&gt;</code> and <code>&lt;use&gt;</code> elements are among the most elegant but sometimes frustrating aspects of SVG. The Shadow DOM barrier makes animating them trickier, but <strong>CSS Custom Properties act as a bridge</strong>. They let you pass colour, motion, and personality across that invisible boundary, resulting in cleaner, lighter, and, best of all, fun animations.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Daniel Schwarz</author><title>How To Leverage Component Variants In Penpot</title><link>https://www.smashingmagazine.com/2025/11/how-leverage-component-variants-penpot/</link><pubDate>Tue, 04 Nov 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/11/how-leverage-component-variants-penpot/</guid><description>With component variants, design systems become more flexible, letting you reuse the same component while adapting its look or state with ease. In this article, Daniel Schwarz demonstrates how design tokens can be leveraged to manage components and their variations using &lt;a href="https://penpot.app?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=Variants">Penpot&lt;/a>, the open-source tool built for scalable, consistent design.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/11/how-leverage-component-variants-penpot/" />
              <title>How To Leverage Component Variants In Penpot</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>How To Leverage Component Variants In Penpot</h1>
                  
                    
                    <address>Daniel Schwarz</address>
                  
                  <time datetime="2025-11-04T10:00:00&#43;00:00" class="op-published">2025-11-04T10:00:00+00:00</time>
                  <time datetime="2025-11-04T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                <p>This article is sponsored by <b>Penpot</b></p>
                

<p>Since Brad Frost popularized the use of design systems in digital design <a href="https://bradfrost.com/blog/post/atomic-web-design/">way back in 2013</a>, they’ve become an invaluable resource for organizations &mdash; and even individuals &mdash; that want to craft reusable design patterns that look and feel consistent.</p>

<p>But Brad didn’t just popularize design systems; he also gave us a <strong>framework</strong> for structuring them, and while we don’t have to follow that framework exactly (most people adapt it to their needs), a particularly important part of most design systems is the <strong>variants</strong>, which are <em>variations</em> of components. Component variants allow for the design of components that are the same as other components, but different, so that they’re understood by users immediately, yet provide clarity for a unique context.</p>

<p>This makes component variants just as important as the components themselves. They ensure that we aren’t creating too many components that have to be individually managed, even if they’re only mildly different from other components, and since component variants are grouped together, they also ensure organization and visual consistency.</p>

<p>And now we can use them in <a href="https://penpot.app?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=Variants">Penpot</a>, the web-based, open-source design tool where design is expressed as code. In this article, you’ll learn about variants, their place in <a href="https://penpot.app/design/design-systems?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=Variants">design systems</a>, and how to use them effectively in Penpot.</p>

<h2 id="step-1-get-your-design-tokens-in-order">Step 1: Get Your Design Tokens In Order</h2>

<p>For the most part, what separates one variant from another is the <a href="https://penpot.app/collaboration/design-tokens?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=DesignTokens">design tokens</a> that it uses. But what is a design token exactly?</p>

<p>Imagine a brand color, let’s say a color value equal to <code>hsl(270 100 42)</code> in CSS. We save it as a “design token” called <code>color.brand.default</code> so that we can reuse it more easily without having to remember the more cumbersome <code>hsl(270 100 42)</code>.</p>

<p>From there, we might also create a second design token called <code>background.button.primary.default</code> and set it to <code>color.brand.default</code>, thereby making them equal to the same color, but with different names to establish semantic separation between the two. This referencing the value of one token from another token is often called an “alias”.</p>

<p>This setup gives us the flexibility to change the value of the color document-wide, change the color used in the component (maybe by switching to a different token alias), or create a variant of the component that uses a different color. Ultimately, the goal is to be able to make changes in many places at once rather than one-by-one, mostly by editing the design token values rather than the design itself, at specific scopes rather than limiting ourselves to all-or-nothing changes. This also enables us to scale our design system without constraints.</p>

<p>With that in mind, here’s a rough idea of just a few color-related design tokens for a primary button with hover and disabled states:</p>

<table class="tablesaw break-out">
    <thead>
        <tr>
            <th>Token name</th>
            <th>Token value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>color.brand.default</code></td>
            <td><code>hsl(270 100 42)</code></td>
        </tr>
        <tr>
            <td><code>color.brand.lighter</code></td>
            <td><code>hsl(270 100 52)</code></td>
        </tr>
        <tr>
            <td><code>color.brand.lightest</code></td>
            <td><code>hsl(270 100 95)</code></td>
        </tr>
    <tr>
            <td><code>color.brand.muted</code></td>
            <td><code>hsl(270 5 50)</code></td>
        </tr>
    <tr>
            <td><code>background.button.primary.default</code></td>
            <td><code>{color.brand.default}</code></td>
        </tr>
    <tr>
            <td><code>background.button.primary.hover</code></td>
            <td><code>{color.brand.lighter}</code></td>
        </tr>
    <tr>
            <td><code>background.button.primary.disabled</code></td>
            <td><code>{color.brand.muted}</code></td>
        </tr>
    <tr>
            <td><code>text.button.primary.default</code></td>
            <td><code>{color.brand.lightest}</code></td>
        </tr>
    <tr>
            <td><code>text.button.primary.hover</code></td>
            <td><code>{color.brand.lightest}</code></td>
        </tr>
     <tr>
            <td><code>text.button.primary.disabled</code></td>
            <td><code>{color.brand.lightest}</code></td>
        </tr>
    </tbody>
</table>

<p>To create a color token in Penpot, switch to the “Tokens” tab in the left panel, click on the plus (<code>+</code>) icon next to “Color”, then specify the name, value, and optional description.</p>

<p>For example:</p>

<ul>
<li><strong>Name</strong>: <code>color.brand.default</code>,</li>
<li><strong>Value</strong>: <code>hsl(270 100 42)</code> (there’s a color picker if you need it).</li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png"
			
			sizes="100vw"
			alt="Creating a color token in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Creating a color token in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/1-color-token-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>It’s pretty much the same process for other types of design tokens.</p>

<p>Don’t worry, I’m not going to walk you through every design token, but I will show you how to create a design token <em>alias</em>. Simply repeat the steps above, but for the value, notice how I’ve just referenced another color token (make sure to include the curly braces):</p>

<ul>
<li><strong>Name</strong>: <code>background.button.primary.default</code>,</li>
<li><strong>Value</strong>: <code>{color.brand.default}</code></li>
</ul>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png"
			
			sizes="100vw"
			alt="Creating a design token alias in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Creating a design token alias in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/2-design-token-alias-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Now, if the value of the color changes, so will the background of the buttons. But also, if we want to decouple the color from the buttons, all we need to do is reference a different color token or value. Mikołaj Dobrucki goes into a lot more detail in another <a href="https://www.smashingmagazine.com/2025/05/integrating-design-code-native-design-tokens-penpot/">Smashing article</a>, but it’s worth noting here that Penpot design tokens are platform-agnostic. They follow the standardized <a href="https://www.w3.org/community/design-tokens/">W3C DTCG format</a>, which means that they’re compatible with other tools and easily export to all platforms, including web, iOS, and Android.</p>

<p>In the next couple of steps, we’ll create a button component and its variants while plugging different design tokens into different variants. You’ll see why doing this is so useful and how using design tokens in variants benefits design systems overall.</p>

<h2 id="step-2-create-the-component">Step 2: Create The Component</h2>

<p>You’ll need to create what’s called a “main” component, which is the one that you’ll update as needed going forward. Other components &mdash; the ones that you’ll actually insert into your designs &mdash; will be copies (or “instances”) of the main component, which is sort of the point, right? Update once, and the changes reflect everywhere.</p>

<p>Here’s one I made earlier, minus the colors:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png"
			
			sizes="100vw"
			alt="Main component"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/3-main-component-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>To apply a design token, make sure that you’re on the “Tokens” tab and have the relevant layer selected, then select the design token that you want to apply to it:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png"
			
			sizes="100vw"
			alt="Applying a design token in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Applying a design token in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/4-design-token-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>It doesn’t matter which variant you create first, but you’ll probably want to go with the default one as a starting point, as I’ve done. Either way, to turn this button into a main component, select the button object via the canvas (or “Layers” tab), right-click on it, then choose the “Create component” option from the context menu (or just press <kbd>Ctrl</kbd> / <kbd>⌘</kbd> + <kbd>K</kbd> after selecting it).</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png"
			
			sizes="100vw"
			alt="Creating a component in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Creating a component in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/5-create-component-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Remember to name the component as well. You can do that by double-clicking on the name (also via the canvas or “Layers” tab).</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png"
			
			sizes="100vw"
			alt="Renaming a component in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Renaming a component in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/6-renaming-component-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="step-3-create-the-component-variants">Step 3: Create The Component Variants</h2>

<p>To create a variant, select the main component and either hit the <kbd>Ctrl</kbd> / <kbd>⌘</kbd> + <kbd>K</kbd> keyboard shortcut, or click on the icon that reveals the “Create variant” tooltip (located in the “Design” tab in the right panel).</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png"
			
			sizes="100vw"
			alt="Creating a component variant in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Creating a component variant in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/7-creating-component-variant-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Next, while the variant is still selected, make the necessary design changes via the “Design” tab. Or, if you want to swap design tokens out for other design tokens, you can do that in the same way that you applied them to begin with, via the “Tokens” tab. Rinse and repeat until you have all of your variants on the canvas designed:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png"
			
			sizes="100vw"
			alt="Styling component variants in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Styling component variants in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/8-styling-component-variants-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>After that, as you might’ve guessed, you’ll want to name your variants. But avoid doing this via the “Layers” panel. Instead, select a variant and replace “Property 1” with a label that describes the differentiating property of each variant. Since my button variants in this example represent different states of the same button, I’ve named this “State”. This applies to all of the variants, so you only need to do this once.</p>

<p>Next to the property name, you’ll see “Value 1” or something similar. Edit that for each variant, for example, the name of the state. In my case, I’ve named them “Default”, “Hover”, and “Disabled”.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png"
			
			sizes="100vw"
			alt="Naming component variant properties in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Naming component variant properties in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/9-naming-component-variant-properties-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>And yes, you can add more properties to a component. To do this, click on the nearby plus (<code>+</code>) icon. I’ll talk more about component variants at scale in a minute, though.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png"
			
			sizes="100vw"
			alt="Managing component variant properties in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Managing component variant properties in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/10-managing-component-variant-properties-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>To see the component in action, switch to the “Assets” tab (located in the left panel) and drag the component onto the canvas to initialize one instance of it. Again, remember to choose the correct property value from the “Design” tab:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="500"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png"
			
			sizes="100vw"
			alt="Using component variants in Penpot"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Using component variants in Penpot. (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/11-using-component-variants-penpot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>If you already have a <a href="https://penpot.app/blog/penpot-for-design-systems-101/?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=DesignTokens">Penpot design system</a>, combining multiple components into one component with variants is not only easy and error-proof, but you might be good to go already if you’re using a robust property naming system that uses forward slashes (<code>/</code>). Penpot has put together <a href="https://community.penpot.app/t/how-to-prepare-your-files-for-the-upcoming-variants-release/9804?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=DesignTokens">a very straightforward guide</a>, but the diagram below sums it up pretty well:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="463"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png"
			
			sizes="100vw"
			alt="Diagram on how to prepare your files for the upcoming Variants release"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/how-leverage-component-variants-penpot/12-diagram-sorting.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="how-component-variants-work-at-scale">How Component Variants Work At Scale</h2>

<p>Design tokens, components, and component variants &mdash; the triple-threat of design systems &mdash; work together, not just to create powerful yet flexible design systems, but sustainable design systems that scale. This is easier to accomplish when thinking ahead, starting with design tokens that separate the “what” from the “what for” using token aliases, despite how verbose that might seem at first.</p>

<p>For example, I used <code>color.brand.lightest</code> for the text color of every variant, but instead of plugging that color token in directly, I created aliases such as <code>text.button.primary.default</code>. This means that I can change the text color of any variant later without having to dive into the actual variant on the canvas, or force a change to <code>color.brand.lightest</code> that might impact a bunch of other components.</p>

<p>Because remember, while the component and its variants give us reusability of the button, <strong>the color tokens give us reusability of the colors</strong>, which might be used in dozens, if not hundreds, of other components. A design system is like a living, breathing ecosystem, where some parts of it are connected, some parts of it aren’t connected, and some parts of it are or aren’t connected but might have to be later, and we need to be ready for that.</p>

<p>The good news is that Penpot makes all of this pretty easy to manage as long as you do a little planning beforehand.</p>

<p>Consider the following:</p>

<ul>
<li>The design tokens that you’ll reuse (e.g., colors, font sizes, and so on),</li>
<li>Where design token aliases will be reused (e.g., buttons, headings, and so on),</li>
<li>Organizing the design tokens into <a href="https://help.penpot.app/user-guide/design-tokens/?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=Variants#design-tokens-sets">sets</a>,</li>
<li>Organizing the sets into <a href="https://help.penpot.app/user-guide/design-tokens/?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=Variants#design-tokens-themes">themes</a>,</li>
<li>Organizing the themes into <a href="https://help.penpot.app/user-guide/design-tokens/?utm_source=SmashingMag&amp;utm_medium=Article&amp;utm_campaign=Variants#design-tokens-themes-group">groups</a>,</li>
<li>The different components that you’ll need, and</li>
<li>The different variants and variant properties that you’ll need for each component.</li>
</ul>

<p>Even the buttons that I designed here today can be scaled far beyond what I’ve already mocked up. Think of all the possible variants that might come up, such as a secondary button color, a tertiary color, a confirmation color, a warning color, a cancelled color, different colors for light and dark mode, not to mention more properties for more states, such as active and focus states. What if we want a whole matrix of variants, like where buttons in a disabled state can be hovered and where all buttons can be focused upon? Or where some buttons have icons instead of text labels, or both?</p>

<p>Designs can get very complicated, but once you’ve organized them into design tokens, components, and component variants in Penpot, they’ll actually feel quite simple, especially once you’re able to see them on the canvas, and even more so once you’ve made a significant change in just a few seconds without breaking anything.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This is how we make component variants work at scale. We get the benefits of <strong>reusability</strong> while keeping the <strong>flexibility</strong> to fork any aspect of our design system, big or small, without breaking out of it. And design tools like Penpot make it possible to not only establish a design system, but also express its design tokens and styles as code.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Andy Clarke</author><title>Ambient Animations In Web Design: Practical Applications (Part 2)</title><link>https://www.smashingmagazine.com/2025/10/ambient-animations-web-design-practical-applications-part2/</link><pubDate>Wed, 22 Oct 2025 13:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/10/ambient-animations-web-design-practical-applications-part2/</guid><description>Motion can be tricky: too much distracts, too little feels flat. Ambient animations sit in the middle. They’re subtle, slow-moving details that add atmosphere without stealing the show. In part two of his series, web design pioneer &lt;a href="https://stuffandnonsense.co.uk">Andy Clarke&lt;/a> shows how ambient animations can add personality to any website design.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/10/ambient-animations-web-design-practical-applications-part2/" />
              <title>Ambient Animations In Web Design: Practical Applications (Part 2)</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Ambient Animations In Web Design: Practical Applications (Part 2)</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-10-22T13:00:00&#43;00:00" class="op-published">2025-10-22T13:00:00+00:00</time>
                  <time datetime="2025-10-22T13:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>First, a recap:</p>

<blockquote>Ambient animations are the kind of passive movements you might not notice at first. However, they bring a design to life in subtle ways. Elements might subtly transition between colours, move slowly, or gradually shift position. Elements can appear and disappear, change size, or they could rotate slowly, adding depth to a brand’s personality.</blockquote>

<p>In <a href="https://www.smashingmagazine.com/2025/09/ambient-animations-web-design-principles-implementation/">Part 1</a>, I illustrated the concept of ambient animations by recreating the cover of a Quick Draw McGraw comic book as a CSS/SVG animation. But I know not everyone needs to animate cartoon characters, so in Part 2, I’ll share how ambient animation works in three very different projects: Reuven Herman, Mike Worth, and EPD. Each demonstrates how motion can <strong>enhance brand identity</strong>, <strong>personality</strong>, and <strong>storytelling</strong> without dominating a page.</p>

<h2 id="reuven-herman">Reuven Herman</h2>

<p>Los Angeles-based composer Reuven Herman didn’t just want a website to showcase his work. He wanted it to convey his personality and the experience clients have when working with him. Working with musicians is always creatively stimulating: they’re critical, engaged, and full of ideas.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png"
			
			sizes="100vw"
			alt="Design for LA-based composer Reuven Herman"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      My design for LA-based composer Reuven Herman. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/1-design-reuven-herman.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Reuven’s classical and jazz background reminded me of the work of album cover designer <a href="https://stuffandnonsense.co.uk/blog/a-book-for-your-inspiration-collection-alex-steinweiss">Alex Steinweiss</a>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="267"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png"
			
			sizes="100vw"
			alt="Album cover designs by Alex Steinweiss"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Album cover designs by Alex Steinweiss. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/2-album-cover-designs-alex-steinweiss.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I was inspired by the depth and texture that Alex brought to his designs for over 2,500 unique covers, and I wanted to incorporate his techniques into my illustrations for Reuven.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="267"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png"
			
			sizes="100vw"
			alt="Illustrations for Reuven Herman"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Two of my illustrations for Reuven Herman. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/3-illustrations-reuven-herman.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>To bring Reuven’s illustrations to life, I followed a few core ambient animation principles:</p>

<ul>
<li>Keep animations <strong>slow</strong> and <strong>smooth</strong>.</li>
<li><strong>Loop seamlessly</strong> and avoid abrupt changes.</li>
<li>Use <strong>layering</strong> to build complexity.</li>
<li>Avoid distractions.</li>
<li>Consider <strong>accessibility</strong> and <strong>performance</strong>.</li>
</ul>


<figure class="video-embed-container break-out">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/1129468148"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>You can view this ambient animation <a href="https://stuffandnonsense.co.uk/lab/ambient-animations.html">in my lab</a>. For Reuven’s site:</p>

<ul>
<li>Sheet music stave lines morph between wavy and straight states.</li>
<li>Notes drift at different speeds to create parallax-like depth.</li>
<li>Piano keys appear to float.</li>
</ul>

<p>My first step is always to <a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">optimise my SVGs for animation</a> by exporting and optimising one set of elements at a time &mdash; always in the order they’ll appear in the final file and building the master SVG gradually. Working forwards from the background, I exported the sheet music stave lines, first in their wavy state.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="275"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png"
			
			sizes="100vw"
			alt="Sheet music stave lines (wavy)"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Sheet music stave lines (wavy). (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/4-sheet-music-stave-lines-wavy.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>…followed by their straight state:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png"
			
			sizes="100vw"
			alt="Sheet music stave lines (straight)"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Sheet music stave lines (straight). (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/5-sheet-music-stave-lines-straight.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The first step in my animation is to morph the stave lines between states. They’re made up of six paths with multi-coloured strokes. I started with the wavy lines:</p>

<pre><code class="language-svg">&lt;!-- Wavy state --&gt;
&lt;g fill="none" stroke-width="2" stroke-linecap="round"&gt;
&lt;path id="p1" stroke="#D2AB99" d="[…]"/&gt;
&lt;path id="p2" stroke="#BDBEA9" d="[…]"/&gt;
&lt;path id="p3" stroke="#E0C852" d="[…]"/&gt;
&lt;path id="p4" stroke="#8DB38B" d="[…]"/&gt;
&lt;path id="p5" stroke="#43616F" d="[…]"/&gt;
&lt;path id="p6" stroke="#A13D63" d="[…]"/&gt;
&lt;/g&gt;
</code></pre>

<p>Although <a href="https://www.smashingmagazine.com/2023/10/animate-along-path-css/">CSS now enables animation between path points</a>, the number of points in each state needs to match. <a href="https://gsap.com">GSAP</a> doesn’t have that limitation and can animate between states that have different numbers of points, making it ideal for this type of animation. I defined the new set of straight paths:</p>

<pre><code class="language-javascript">&lt;!-- Straight state --&gt;
const Waves = {
  p1: "[…]",
  p2: "[…]",
  p3: "[…]",
  p4: "[…]",
  p5: "[…]",
  p6: "[…]"
};
</code></pre>

<p>Then, I created a <a href="https://gsap.com/docs/v3/GSAP/Timeline">GSAP timeline</a> that repeats backwards and forwards over six seconds:</p>

<pre><code class="language-javascript">const waveTimeline = gsap.timeline({
  repeat: -1,
  yoyo: true,
  defaults: { duration: 6, ease: "sine.inOut" }
});

Object.entries(Waves).forEach(([id, d]) =&gt; {
  waveTimeline.to(`#${id}`, { morphSVG: d }, 0);
});
</code></pre>

<p><strong>Another ambient animation principle is to use layering to build complexity.</strong> Think of it like building a sound mix. You want variation in rhythm, tone, and timing. In my animation, three rows of musical notes move at different speeds:</p>

<pre><code class="language-svg">&lt;path id="notes-row-1"/&gt;
&lt;path id="notes-row-2"/&gt;
&lt;path id="notes-row-3"/&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="275"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png"
			
			sizes="100vw"
			alt="Three rows of musical notes"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Three rows of musical notes. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/6-three-rows-musical-notes.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The duration of each row’s animation is also defined using GSAP, from <code>100</code> to <code>400</code> seconds to give the overall animation a parallax-style effect:</p>

<pre><code class="language-javascript">const noteRows = [
  { id: "#notes-row-1", duration: 300, y: 100 }, // slowest
  { id: "#notes-row-2", duration: 200, y: 250 }, // medium
  { id: "#notes-row-3", duration: 100, y: 400 }  // fastest
];

[…]
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="275"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png"
			
			sizes="100vw"
			alt="Animated shadow"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Animated shadow. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/7-animated-shadow.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The next layer contains a shadow cast by the piano keys, which slowly rotates around its centre:</p>

<pre><code class="language-javascript">gsap.to("shadow", {
  y: -10,
  rotation: -2,
  transformOrigin: "50% 50%",
  duration: 3,
  ease: "sine.inOut",
  yoyo: true,
  repeat: -1
});
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="275"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png"
			
			sizes="100vw"
			alt="Animated piano keys"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Animated piano keys. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/8-animated-piano-keys.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>And finally, the piano keys themselves, which rotate at the same time but in the opposite direction to the shadow:</p>

<pre><code class="language-javascript">gsap.to("#g3-keys", {
  y: 10,
  rotation: 2,
  transformOrigin: "50% 50%",
  duration: 3,
  ease: "sine.inOut",
  yoyo: true,
  repeat: -1
});
</code></pre>

<p>The complete animation can be viewed <a href="https://stuffandnonsense.co.uk/lab/ambient-animations.html">in my lab</a>. By layering motion thoughtfully, the site feels alive without ever dominating the content, which is a perfect match for Reuven’s energy.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="/printed-books/typescript-in-50-lessons/">“TypeScript in 50 Lessons”</a></strong>, our shiny new guide to TypeScript. With detailed <strong>code walkthroughs</strong>, hands-on examples and common gotchas. For developers who know enough <strong>JavaScript</strong> to be dangerous.</p>
<a data-instant href="/printed-books/typescript-in-50-lessons/" class="btn btn--green btn--large" style="">Jump to table of contents&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="/printed-books/typescript-in-50-lessons/" class="feature-panel-image-link">
<div class="feature-panel-image"><picture><source type="image/avif" srcSet="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/2732dfe9-e1ee-41c3-871a-6252aeda741c/typescript-panel.avif" />
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="https://archive.smashing.media/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/c2f2c6d6-4e85-449a-99f5-58bd053bc846/typescript-shop-cover-opt.png"
    alt="Feature Panel"
    width="481"
    height="698"
/>
</picture>
</div>
</a>
</div>
</aside>
</div>

<h2 id="mike-worth">Mike Worth</h2>

<p>As I mentioned earlier, not everyone needs to animate cartoon characters, but I do occasionally. Mike Worth is an Emmy award-winning film, video game, and TV composer who asked me to design his website. For the project, I created and illustrated the character of orangutan adventurer Orango Jones.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png"
			
			sizes="100vw"
			alt="Design for Mike Worth"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      My design for Emmy award-winning composer Mike Worth. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/9-design-mike-worth.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Orango proved to be the perfect subject for ambient animations and features on every page of Mike’s website. He takes the reader on an adventure, and along the way, they get to experience Mike’s music.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png"
			
			sizes="100vw"
			alt="Illustration for Mike Worth"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Another of my illustrations for Mike Worth. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/10-illustration-mike-worth.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>For Mike’s “About” page, I wanted to combine ambient animations with interactions. Orango is in a cave where he has found a stone tablet with faint markings that serve as a navigation aid to elsewhere on Mike’s website. The illustration contains a hidden feature, an easter egg, as when someone presses Orango’s magnifying glass, moving shafts of light stream into the cave and onto the tablet.</p>


<figure class="video-embed-container break-out">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/1129470404"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>My SVG is deliberately structured, and from back to front, it includes the cave, light shaft, Orango, and navigation:</p>

<pre><code class="language-svg">&lt;svg data-lights="lights-off"&gt;
  &lt;g id="cave"&gt;[…]&lt;/g&gt;
  &lt;path id="light-shaft" d="[…]"&gt;&lt;/path&gt;
  &lt;g id="orango"&gt;[…]&lt;/g&gt;
  &lt;g id="nav"&gt;[…]&lt;/g&gt;
&lt;/svg&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png"
			
			sizes="100vw"
			alt="The cave background"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The cave background. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/11-cave-background.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I also added an anchor around a hidden circle, which I positioned over Orango’s magnifying glass, as a large tap target to toggle the light shafts on and off by changing the <code>data-lights</code> value on the SVG:</p>

<div class="break-out">
<pre><code class="language-html">&lt;a href="javascript:void(0);" id="light-switch" title="Lights on/off"&gt;
  &lt;circle cx="700" cy="1000" r="100" opacity="0" /&gt;
&lt;/a&gt;
</code></pre>
</div>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png"
			
			sizes="100vw"
			alt="Orango isolated"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Orango isolated. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/12-orango-isolated.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Then, I added two descendant selectors to my CSS, which adjust the opacity of the light shafts depending on the <code>data-lights</code> value:</p>

<pre><code class="language-css">[data-lights="lights-off"] .light-shaft {
  opacity: .05;
  transition: opacity .25s linear;
}

[data-lights="lights-on"] .light-shaft {
  opacity: .25;
  transition: opacity .25s linear;
}
</code></pre>

<p>A slow and subtle rotation adds natural movement to the light shafts:</p>

<pre><code class="language-css">@keyframes shaft-rotate {
  0% { rotate: 2deg; }
  50% { rotate: -2deg; }
  100% { rotate: 2deg; }
}
</code></pre>

<p>Which is only visible when the light toggle is active:</p>

<pre><code class="language-css">[data-lights="lights-on"] .light-shaft {
  animation: shaft-rotate 20s infinite;
  transform-origin: 100% 0;
}
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png"
			
			sizes="100vw"
			alt="Light shafts isolated"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Light shafts isolated. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/13-light-shafts-isolated.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>When developing any ambient animation, considering performance is crucial, as even though CSS animations are lightweight, features like blur filters and drop shadows can still strain lower-powered devices. It’s also critical to consider accessibility, so <a href="https://www.smashingmagazine.com/2021/10/respecting-users-motion-preferences/">respect someone’s <code>prefers-reduced-motion</code> preferences</a>:</p>

<pre><code class="language-css">@media screen and (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}
</code></pre>

<p>When an animation feature is purely decorative, consider adding <code>aria-hidden=&quot;true&quot;</code> to keep it from cluttering up the accessibility tree:</p>

<pre><code class="language-html">&lt;a href="javascript:void(0);" id="light-switch" aria-hidden="true"&gt;
  […]
&lt;/a&gt;
</code></pre>

<p>With Mike’s Orango Jones, ambient animation shifts from subtle atmosphere to playful storytelling. Light shafts and soft interactions weave narrative into the design without stealing focus, proving that animation can support both brand identity and user experience. See this animation <a href="https://stuffandnonsense.co.uk/lab/ambient-animations.html">in my lab</a>.</p>

<div class="partners__lead-place"></div>

<h2 id="epd">EPD</h2>

<p>Moving away from composers, EPD is a property investment company. They commissioned me to design creative concepts for a new website. A quick search for property investment companies will usually leave you feeling underwhelmed by their interchangeable website designs. They include full-width banners with faded stock photos of generic city skylines or ethnically diverse people shaking hands.</p>

<p>For EPD, I wanted to develop a distinctive visual style that the company could own, so I proposed graphic, stylised skylines that reflect both EPD’s brand and its global portfolio. I made them using various-sized circles that recall the company’s logo mark.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png"
			
			sizes="100vw"
			alt="Design for the property investment company"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      My design for the property investment company EPD. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/14-design-epd.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The point of an ambient animation is that it doesn’t dominate. It’s a background element and not a call to action. If someone’s eyes are drawn to it, it’s probably too much, so I dial back the animation until it feels like something you’d only catch if you’re really looking. I created three skyline designs, including Dubai, London, and Manchester.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="208"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png"
			
			sizes="100vw"
			alt="Illustrations showing the skylines of Manchester and London"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Manchester and London. Two of my illustrations for EPD. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/15-design-manchester-london.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>In each of these ambient animations, the wheels rotate and the large circles change colour at random intervals.</p>


<figure class="video-embed-container break-out">
  <div class="video-embed-container--wrapper"
	
  >
    <iframe class="video-embed-container--wrapper-iframe" src="https://player.vimeo.com/video/1129472862"
        frameborder="0"
        allow="autoplay; fullscreen; picture-in-picture"
        allowfullscreen>
    </iframe>
	</div>
	
</figure>

<p>To begin optimising this illustration for animation, I exported the base paths containing every element except the wheel:</p>

<pre><code class="language-svg">&lt;g id="banner-base&gt;
  &lt;path d="[…]"/&gt;
  &lt;path d="[…]"/&gt;
  &lt;path d="[…]"/&gt;
  […]
&lt;/g&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="195"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png"
			
			sizes="100vw"
			alt="Manchester illustration base layer"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      My Manchester illustration base layer. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/16-manchester-illustration-base-layer.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Next, I exported a layer containing the <code>circle</code> elements I want to change colour.</p>

<pre><code class="language-svg">&lt;g id="banner-dots"&gt;
  &lt;circle class="data-theme-fill" […]/&gt;
  &lt;circle class="data-theme-fill" […]/&gt;
  &lt;circle class="data-theme-fill" […]/&gt;
  […]
&lt;/g&gt;
</code></pre>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="195"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png"
			
			sizes="100vw"
			alt="Random-looking circles in Manchester illustration"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Random-looking circles in my Manchester illustration. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/17-circles-manchester-illustration.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Once again, I used GSAP to select groups of circles that flicker like lights across the skyline:</p>

<div class="break-out">
<pre><code class="language-javascript">function animateRandomDots() {
  const circles = gsap.utils.toArray("#banner-dots circle")
  const numberToAnimate = gsap.utils.random(3, 6, 1)
  const selected = gsap.utils.shuffle(circles).slice(0, numberToAnimate)
}
</code></pre>
</div>

<p>Then, at two-second intervals, the <code>fill</code> colour of those circles changes from the teal accent to the same off-white colour as the rest of my illustration:</p>

<pre><code class="language-javascript">gsap.to(selected, {
  fill: "color(display-p3 .439 .761 .733)",
  duration: 0.3,
  stagger: 0.05,
  onComplete: () =&gt; {
    gsap.to(selected, {
      fill: "color(display-p3 .949 .949 .949)",
      duration: 0.5,
      delay: 2
    })
  }
})

gsap.delayedCall(gsap.utils.random(1, 3), animateRandomDots) }
animateRandomDots()
</code></pre>

<p>The result is a skyline that gently flickers, as if the city itself is alive. Finally, I rotated the wheel. Here, there was no need to use GSAP as this is possible using CSS <code>rotate</code> alone:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;g id="banner-wheel"&gt;
  &lt;path stroke="#F2F2F2" stroke-linecap="round" stroke-width="4" d="[…]"/&gt;
  &lt;path fill="#D8F76E" d="[…]"/&gt;
&lt;/g&gt;
</code></pre>
</div>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="195"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png"
			
			sizes="100vw"
			alt="Rotating wheel in Manchester illustration"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Rotating wheel in my Manchester illustration. (<a href='https://files.smashing.media/articles/ambient-animations-web-design-practical-applications-part2/18-rotating-wheel-manchester-illustration.png'>Large preview</a>)
    </figcaption>
  
</figure>

<pre><code class="language-css">
#banner-wheel {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: rotateWheel 30s linear infinite;
}

@keyframes rotateWheel {
  to { transform: rotate(360deg); }
}
</code></pre>

<p>CSS animations are lightweight and ideal for simple, repetitive effects, like fades and rotations. They’re easy to implement and don’t require libraries. GSAP, on the other hand, offers far more control as it can handle path morphing and sequence timelines. The choice of which to use depends on whether I need the <strong>precision of GSAP</strong> or the <strong>simplicity of CSS</strong>.</p>

<p>By keeping the wheel turning and the circles glowing, the skyline animations stay in the background yet give the design a distinctive feel. They avoid stock photo clichés while reinforcing EPD’s brand identity and are proof that, even in a conservative sector like property investment, ambient animation can add atmosphere without detracting from the message.</p>

<div class="partners__lead-place"></div>

<h2 id="wrapping-up">Wrapping up</h2>

<p>From Reuven’s musical textures to Mike’s narrative-driven Orango Jones and EPD’s glowing skylines, these projects show how <strong>ambient animation</strong> adapts to context. Sometimes it’s purely atmospheric, like drifting notes or rotating wheels; other times, it blends seamlessly with interaction, rewarding curiosity without getting in the way.</p>

<p>Whether it echoes a composer’s improvisation, serves as a playful narrative device, or adds subtle distinction to a conservative industry, the same principles hold true:</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aKeep%20motion%20slow,%20seamless,%20and%20purposeful%20so%20that%20it%20enhances,%20rather%20than%20distracts%20from,%20the%20design.%0a&url=https://smashingmagazine.com%2f2025%2f10%2fambient-animations-web-design-practical-applications-part2%2f">
      
Keep motion slow, seamless, and purposeful so that it enhances, rather than distracts from, the design.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Vitaly Friedman</author><title>How To Make Your UX Research Hard To Ignore</title><link>https://www.smashingmagazine.com/2025/10/how-make-ux-research-hard-to-ignore/</link><pubDate>Thu, 16 Oct 2025 13:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/10/how-make-ux-research-hard-to-ignore/</guid><description>Research isn’t everything. Facts alone don’t win arguments, but powerful stories do. Here’s how to turn your research into narratives that inspire trust and influence decisions.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/10/how-make-ux-research-hard-to-ignore/" />
              <title>How To Make Your UX Research Hard To Ignore</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>How To Make Your UX Research Hard To Ignore</h1>
                  
                    
                    <address>Vitaly Friedman</address>
                  
                  <time datetime="2025-10-16T13:00:00&#43;00:00" class="op-published">2025-10-16T13:00:00+00:00</time>
                  <time datetime="2025-10-16T13:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>In the early days of my career, I believed that nothing <strong>wins an argument</strong> more effectively than strong and unbiased research. Surely facts speak for themselves, I thought.</p>

<p>If I just get enough data, just enough evidence, just enough clarity on where users struggle &mdash; well, once I have it all and I present it all, it alone will surely change people’s minds, hearts, and beliefs. And, most importantly, it will help everyone see, understand, and perhaps even appreciate and commit to <strong>what needs to be done</strong>.</p>

<p>Well, it’s not quite like that. In fact, the stronger and louder the data, the more likely it is to be <strong>questioned</strong>. And there is a good reason for that, which is often left between the lines.</p>

<h2 id="research-amplifies-internal-flaws">Research Amplifies Internal Flaws</h2>

<p>Throughout the years, I’ve often seen data speaking volumes about where the business is failing, where customers are struggling, where the team is faltering &mdash; and where an <strong>urgent turnaround</strong> is necessary. It was right there, in plain sight: clear, loud, and obvious.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://medium.com/shopify-ux/the-design-process-is-a-lie-465a7064a733">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="600"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/1-illustration-jose-torre.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/1-illustration-jose-torre.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/1-illustration-jose-torre.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/1-illustration-jose-torre.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/1-illustration-jose-torre.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/1-illustration-jose-torre.jpg"
			
			sizes="100vw"
			alt="Illustration by José Torre."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Good research doesn't just uncover troubles; it also amplifies internal flaws and poor decisions. Wonderful illustration by <a href='https://medium.com/shopify-ux/the-design-process-is-a-lie-465a7064a733'>José Torre</a>. (<a href='https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/1-illustration-jose-torre.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But because it’s so clear, it reflects back, often amplifying all the sharp edges and all the cut corners in all the wrong places. It reflects internal flaws, <strong>wrong assumptions</strong>, and failing projects &mdash; some of them signed off years ago, with secured budgets, big promotions, and approved headcounts. Questioning them means <strong>questioning authority</strong>, and often it’s a tough path to take.</p>

<p>As it turns out, strong data is very, very good at raising <strong>uncomfortable truths</strong> that most companies don’t really want to acknowledge. That’s why, at times, research is deemed “unnecessary,” or why we don’t get access to users, or why <strong>loud voices</strong> always win big arguments.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg"
			
			sizes="100vw"
			alt="UX Research in B2B."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      UX Research in B2B: when you don’t have access to users. (<a href='https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/2-ux-research-b2b.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>So even if data is presented with a lot of eagerness, gravity, and passion in that big meeting, it will get questioned, doubted, and explained away. Not because of its flaws, but because of hope, reluctance to change, and layers of <strong>internal politics</strong>.</p>

<p>This shows up most vividly in situations when someone raises concerns about the <strong>validity and accuracy</strong> of research. Frankly, it’s not that somebody is wrong and somebody is right. Both parties just happen to be <strong>right in a different way</strong>.</p>

<h2 id="what-to-do-when-data-disagrees">What To Do When Data Disagrees</h2>

<p>We’ve all heard that data always tells a story. However, it’s <strong>never just a single story</strong>. People are complex, and pointing out a specific truth about them just by looking at numbers is rarely enough.</p>

<p>When data disagrees, it doesn’t mean that either is wrong. It’s just that <strong>different perspectives</strong> reveal different parts of a whole story that isn’t completed yet.</p>














<figure class="
  
  
  ">
  
    <a href="https://medium.com/lexisnexis-design/what-to-do-when-qual-and-quant-disagree-18a535164ca6">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="972"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/3-qual-quant-data.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/3-qual-quant-data.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/3-qual-quant-data.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/3-qual-quant-data.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/3-qual-quant-data.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/3-qual-quant-data.jpg"
			
			sizes="100vw"
			alt="Various UX Research methods"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      <a href='https://medium.com/lexisnexis-design/what-to-do-when-qual-and-quant-disagree-18a535164ca6'>What to do when qual and quant disagree</a>, a very practical guide by Archana Shah. (<a href='https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/3-qual-quant-data.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>In digital products, most stories have <strong>2 sides</strong>:</p>

<ul>
<li><strong>Quantitative data</strong> ← What/When: behavior patterns at scale.</li>
<li><strong>Qualitative data</strong> ← Why/How: user needs and motivations.</li>
<li>↳ Quant usually comes from analytics, surveys, and experiments.</li>
<li>↳ Qual comes from tests, observations, and open-ended surveys.</li>
</ul>

<p>Risk-averse teams overestimate the <strong>weight of big numbers</strong> in quantitative research. Users exaggerate the frequency and severity of issues that are critical for them. As Archana Shah <a href="https://medium.com/lexisnexis-design/what-to-do-when-qual-and-quant-disagree-18a535164ca6">noted</a>, designers get carried away by users’ <strong>confident responses</strong> and often overestimate what people say and do.</p>

<p>And so, eventually, data coming from different teams paints a different picture. And when it happens, we need to <strong>reconcile and triangulate</strong>. With the former, we track what’s missing, omitted, or overlooked. With the latter, we <strong>cross-validate data</strong> &mdash; e.g., finding pairings of qual/quant streams of data, then clustering them together to see what’s there and what’s missing, and exploring from there.</p>

<p>And even with all of it in place and data conflicts resolved, we still need to do one more thing to make a strong argument: we need to tell a <strong>damn good story</strong>.</p>

<h2 id="facts-don-t-win-arguments-stories-do">Facts Don’t Win Arguments, Stories Do</h2>

<p>Research isn’t everything. <a href="https://www.linkedin.com/posts/erikahall_tapping-the-sign-again-every-time-i-see-activity-7360805865051865090-uldg">Facts don’t win arguments</a> &mdash; <strong>powerful stories do</strong>. But a story that starts with a spreadsheet isn’t always inspiring or effective. Perhaps it brings a problem into the spotlight, but it doesn’t lead to a resolution.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://medium.com/shopify-ux/the-design-process-is-a-lie-465a7064a733">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="600"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/4-illustration-jose-torre.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/4-illustration-jose-torre.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/4-illustration-jose-torre.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/4-illustration-jose-torre.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/4-illustration-jose-torre.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/4-illustration-jose-torre.png"
			
			sizes="100vw"
			alt="Illustration by José Torre."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Presenting research is more than presenting findings. It must be wrapped inside an actionable story. Wonderful illustration by <a href='https://medium.com/shopify-ux/the-design-process-is-a-lie-465a7064a733'>José Torre</a>. (<a href='https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/4-illustration-jose-torre.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The very first thing I try to do in that big boardroom meeting is to emphasize <strong>what unites us</strong> &mdash; shared goals, principles, and commitments that are relevant to the topic at hand. Then, I show how new data <strong>confirms or confronts</strong> our commitments, with specific problems we believe we need to address.</p>

<p>When a question about the quality of data comes in, I need to show that it has been <strong>reconciled and triangulated</strong> already and discussed with other teams as well.</p>

<p>A good story has a poignant ending. People need to see an <strong>alternative future</strong> to trust and accept the data &mdash; and a clear and safe path forward to commit to it. So I always try to present options and solutions that we believe will drive change and explain our decision-making behind that.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://ucdc.therectangles.com">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="485"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/5-art-interviewing-stakeholders.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/5-art-interviewing-stakeholders.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/5-art-interviewing-stakeholders.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/5-art-interviewing-stakeholders.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/5-art-interviewing-stakeholders.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/5-art-interviewing-stakeholders.png"
			
			sizes="100vw"
			alt="User Centered Design Canvas"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      A useful little helper to understand what stakeholders truly care about. <a href='https://ucdc.therectangles.com'>User Centered Design Canvas</a> could be applied to stakeholders. (<a href='https://files.smashing.media/articles/how-make-ux-research-hard-to-ignore/5-art-interviewing-stakeholders.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>They also need to believe that this distant future is <strong>within reach</strong>, and that they can pull it off, albeit under a tough timeline or with limited resources.</p>

<p>And: a good story also presents a viable, compelling, <strong>shared goal</strong> that people can rally around and commit to. Ideally, it’s something that has a direct benefit for them and their teams.</p>

<p>These are the ingredients of the story that I always try to keep in my mind when working on that big presentation. And in fact, data is a <strong>starting point</strong>, but it does need a story wrapped around it to be effective.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>There is nothing more disappointing than finding a real problem that real people struggle with and facing the harsh reality of research <strong>not being trusted</strong> or valued.</p>

<p>We’ve all been there before. The best thing you can do is to <strong>be prepared</strong>: have strong data to back you up, include both quantitative and qualitative research &mdash; preferably with video clips from real customers &mdash; but also paint a <strong>viable future</strong> which seems within reach.</p>

<p>And sometimes nothing changes until <strong>something breaks</strong>. And at times, there isn’t much you can do about it unless you are prepared when it happens.</p>

<blockquote>“Data doesn’t change minds, and facts don’t settle fights. Having answers isn’t the same as learning, and it for sure isn’t the same as making evidence-based decisions.”<br /><br />&mdash; Erika Hall</blockquote>

<h2 id="meet-how-to-measure-ux-and-design-impact">Meet “How To Measure UX And Design Impact”</h2>

<p>You can find more details on <strong>UX Research</strong> in <a href="https://measure-ux.com/"><strong>Measure UX &amp; Design Impact</strong></a> (8h), a practical guide for designers and UX leads to measure and show your UX impact on business. Use the code 🎟 <code>IMPACT</code> to save 20% off today. <a href="https://measure-ux.com/">Jump to the details</a>.</p>

<figure style="margin-bottom:0;padding-bottom:0" class="article__image">
    <a href="https://measure-ux.com/" title="How To Measure UX and Design Impact, with Vitaly Friedman">
    <img width="900" height="466" style="border-radius: 11px" src="https://files.smashing.media/articles/ux-metrics-video-course-release/measure-ux-and-design-impact-course.png" alt="How to Measure UX and Design Impact, with Vitaly Friedman.">
    </a>
</figure>

<div class="book-cta__inverted"><div class="book-cta" data-handler="ContentTabs" data-mq="(max-width: 480px)"><nav class="content-tabs content-tabs--books"><ul><li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">
Video + UX Training</button></a></li><li class="content-tab"><a href="#"><button class="btn btn--small btn--white btn--white--bordered">Video only</button></a></li></ul></nav><div class="book-cta__col book-cta__hardcover content-tab--content"><h3 class="book-cta__title"><span>Video + UX Training</span></h3><span class="book-cta__price"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>495<span class="sup">.00</span></span></span> <span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>799<span class="sup">.00</span></span></span></span></span>
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3951439" class="btn btn--full btn--medium btn--text-shadow">
Get Video + UX Training<div></div></a><p class="book-cta__desc">25 video lessons (8h) + <a href="https://smashingconf.com/online-workshops/workshops/vitaly-friedman-impact-design/">Live UX Training</a>.<br>100 days money-back-guarantee.</p></div><div class="book-cta__col book-cta__ebook content-tab--content"><h3 class="book-cta__title"><span>Video only</span></h3><div data-audience="anonymous free supporter" data-remove="true"><span class="book-cta__price" data-handler="PriceTag"><span><span class=""><span class="currency-sign">$</span>&nbsp;<span>250<span class="sup">.00</span></span></span><span class="book-cta__price--old"><span class="currency-sign">$</span>&nbsp;<span>395<span class="sup">.00</span></span></span></span></div>
<a href="https://smart-interface-design-patterns.thinkific.com/enroll/3081832?price_id=3950630" class="btn btn--full btn--medium btn--text-shadow">
Get the video course<div></div></a><p class="book-cta__desc" data-audience="anonymous free supporter" data-remove="true">25 video lessons (8h). Updated yearly.<br>Also available as a <a href="https://smart-interface-design-patterns.thinkific.com/enroll/3082557?price_id=3951421">UX Bundle with 2 video courses.</a></p></div><span></span></div></div>

<h2 id="useful-resources">Useful Resources</h2>

<ul>
<li>“<a href="https://www.dscout.com/people-nerds/present-research-for-stakeholders-tips">How to Present Research So Stakeholders Sit Up and Take Action</a>”, by Nikki Anderson</li>
<li>“<a href="https://medium.com/lexisnexis-design/what-to-do-when-qual-and-quant-disagree-18a535164ca6">What To Do When Data Disagrees</a>”, by Subhasree Chatterjee, Archana Shah, Sanket Shukl, and Jason Bressler</li>
<li>“<a href="https://medium.com/shopify-ux/how-to-use-mixed-method-research-to-drive-product-decisions-7ff023e5b107">Mixed-Method UX Research</a>”, by Raschin Fatemi</li>
<li>“<a href="https://medium.com/@jwill7378/confidently-step-into-mixed-method-ux-research-a-step-by-step-framework-for-mixed-method-research-98f4284b8ebe">A Step-by-Step Framework For Mixed-Method Research</a>”, by Jeremy Williams</li>
<li>“<a href="https://dscout.com/people-nerds/mixed-methods-research">The Ultimate Guide To Mixed Methods</a>”, by Ben Wiedmaier</li>
<li><a href="https://www.linkedin.com/posts/vitalyfriedman_ux-surveys-activity-7222861773375180800-O0c0">Survey Design Cheatsheet</a>, by yours truly</li>
<li><a href="https://www.linkedin.com/posts/vitalyfriedman_ux-design-research-activity-7227973209839538177-P3iV">Useful Calculators For UX Research</a>, by yours truly</li>
<li><a href="https://vimeo.com/188285898?fl=pl&amp;fe=vl">Beyond Measure</a>, by Erika Hall</li>
</ul>

<p><strong>Useful Books</strong></p>

<ul>
<li><em>Just Enough Research</em>, by Erika Hall</li>
<li><em>Designing Surveys That Work</em>, by Caroline Jarrett</li>
<li><em>Designing Quality Survey Questions</em>, by Sheila B. Robinson</li>
</ul>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Frederick O’Brien</author><title>The Grayscale Problem</title><link>https://www.smashingmagazine.com/2025/10/the-grayscale-problem/</link><pubDate>Mon, 13 Oct 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/10/the-grayscale-problem/</guid><description>From A/B tests to AI slop, the modern web is bleeding out its colour. Standardized, templated, and overoptimized, it’s starting to feel like a digital Levittown. But it doesn’t have to be.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/10/the-grayscale-problem/" />
              <title>The Grayscale Problem</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>The Grayscale Problem</h1>
                  
                    
                    <address>Frederick O’Brien</address>
                  
                  <time datetime="2025-10-13T10:00:00&#43;00:00" class="op-published">2025-10-13T10:00:00+00:00</time>
                  <time datetime="2025-10-13T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>Last year, a study found that <a href="https://www.forbes.com/sites/kbrauer/2024/07/16/where-have-all-the-colorful-cars-gone-study-shows-them-vanishing/">cars are steadily getting less colourful</a>. In the US, around 80% of cars are now black, white, gray, or silver, up from 60% in 2004. This trend has been attributed to cost savings and consumer preferences. Whatever the reasons, the result is hard to deny: a big part of daily life isn’t as colourful as it used to be.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="420"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/the-grayscale-problem/1-car-color-market-share.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The colourfulness of mass consumer products is hardly the bellwether for how vibrant life is as a whole, but the study captures a trend a lot of us recognise &mdash; offline and on. From colour to design to public discourse, a lot of life is getting less varied, more grayscale.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="580"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/the-grayscale-problem/2-grayscale-car-models.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The web is caught in the same current. There is plenty right with it &mdash; it retains plenty of its founding principles &mdash; but its state is not healthy. From AI slop to shoddy service providers to enshittification, the digital world faces its own <strong>grayscale problem</strong>.</p>

<p>This bears talking about. One of life’s great fallacies is that things get better over time on their own. They can, but it’s certainly not a given. I don’t think the moral arc of the universe does not bend towards justice, not on its own; I think it bends wherever it is dragged, kicking and screaming, by those with the will and the means to do so.</p>

<p>Much of the modern web, and the forces of optimisation and standardisation that drive it, bear an uncanny resemblance to the trend of car colours. Processes like market research and A/B testing &mdash; <a href="https://hbr.org/2017/06/a-refresher-on-ab-testing">the process by which two options are compared to see which ‘performs’ better on clickthrough, engagement, etc.</a> &mdash; have their value, but they don’t lend themselves to particularly stimulating design choices.</p>

<p>The spirit of free expression that made the formative years of the internet so exciting &mdash; think GeoCities, personal blogging, and so on &mdash; is on the slide.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="416"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png"
			
			sizes="100vw"
			alt="Screenshot from the Geocities Gallery"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Image source: <a href='https://geocities.restorativland.org/'>The Geocities Gallery</a>. (<a href='https://files.smashing.media/articles/the-grayscale-problem/3-geocities.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The ongoing transition to a more decentralised, privacy-aware <a href="https://aws.amazon.com/what-is/web3/">Web3</a> holds some promise. Two-thirds of the world’s population now has online access &mdash; <a href="https://www.weforum.org/stories/2024/01/digital-divide-internet-access-online-fwa/">though that still leaves plenty of work to do</a> &mdash; with a wealth of platforms allowing billions of people to connect. The dream of a digital world that is open, connected, and flat endures, but is tainted.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p><p>Meet <a data-instant href="/the-smashing-newsletter/"><strong>Smashing Email Newsletter</strong></a> with useful tips on front-end, design &amp; UX. Subscribe and <strong>get “Smart Interface Design Checklists”</strong> &mdash; a <strong>free PDF deck</strong> with 150+ questions to ask yourself when designing and building almost <em>anything</em>.</p><div><section class="nlbf"><form action="//smashingmagazine.us1.list-manage.com/subscribe/post?u=16b832d9ad4b28edf261f34df&amp;id=a1666656e0" method="post"><div class="nlbwrapper"><label for="mce-EMAIL-hp" class="sr-only">Your (smashing) email</label><div class="nlbgroup"><input type="email" name="EMAIL" class="nlbf-email" id="mce-EMAIL-hp" placeholder="Your email">
<input type="submit" value="Meow!" name="subscribe" class="nlbf-button"></div></div></form><style>.c-garfield-the-cat .nlbwrapper{margin-bottom: 0;}.nlbf{display:flex;padding-bottom:.25em;padding-top:.5em;text-align:center;letter-spacing:-.5px;color:#fff;font-size:1.15em}.nlbgroup:hover{box-shadow:0 1px 7px -5px rgba(50,50,93,.25),0 3px 16px -8px rgba(0,0,0,.3),0 -6px 16px -6px rgba(0,0,0,.025)}.nlbf .nlbf-button,.nlbf .nlbf-email{flex-grow:1;flex-shrink:0;width:auto;margin:0;padding:.75em 1em;border:0;border-radius:11px;background:#fff;font-size:1em;box-shadow:none}.promo-box .nlbf-button:focus,.promo-box input.nlbf-email:active,.promo-box input.nlbf-email:focus{box-shadow:none}.nlbf-button:-ms-input-placeholder,.nlbf-email:-ms-input-placeholder{color:#777;font-style:italic}.nlbf-button::-webkit-input-placeholder,.nlbf-email::-webkit-input-placeholder{color:#777;font-style:italic}.nlbf-button:-ms-input-placeholder,.nlbf-button::-moz-placeholder,.nlbf-button::placeholder,.nlbf-email:-ms-input-placeholder,.nlbf-email::-moz-placeholder,.nlbf-email::placeholder{color:#777;font-style:italic}.nlbf .nlbf-button{transition:all .2s ease-in-out;color:#fff;background-color:#0168b8;font-weight:700;box-shadow:0 1px 1px rgba(0,0,0,.3);width:100%;border:0;border-left:1px solid #ddd;flex:2;border-top-left-radius:0;border-bottom-left-radius:0}.nlbf .nlbf-email{border-top-right-radius:0;border-bottom-right-radius:0;width:100%;flex:4;min-width:150px}@media all and (max-width:650px){.nlbf .nlbgroup{flex-wrap:wrap;box-shadow:none}.nlbf .nlbf-button,.nlbf .nlbf-email{border-radius:11px;border-left:none}.nlbf .nlbf-email{box-shadow:0 13px 27px -5px rgba(50,50,93,.25),0 8px 16px -8px rgba(0,0,0,.3),0 -6px 16px -6px rgba(0,0,0,.025);min-width:100%}.nlbf .nlbf-button{margin-top:1em;box-shadow:0 1px 1px rgba(0,0,0,.5)}}.nlbf .nlbf-button:active,.nlbf .nlbf-button:focus,.nlbf .nlbf-button:hover{cursor:pointer;color:#fff;background-color:#0168b8;border-color:#dadada;box-shadow:0 1px 1px rgba(0,0,0,.3)}.nlbf .nlbf-button:active,.nlbf .nlbf-button:focus{outline:0!important;text-shadow:1px 1px 1px rgba(0,0,0,.3);box-shadow:inset 0 3px 3px rgba(0,0,0,.3)}.nlbgroup{display:flex;box-shadow:0 13px 27px -5px rgba(50,50,93,.25),0 8px 16px -8px rgba(0,0,0,.3),0 -6px 16px -6px rgba(0,0,0,.025);border-radius:11px;transition:box-shadow .2s ease-in-out}.nlbwrapper{display:flex;flex-direction:column;justify-content:center}.nlbf form{width:100%}.nlbf .nlbgroup{margin:0}.nlbcaption{font-size:.9em;line-height:1.5em;color:#fff;border-radius:11px;padding:.5em 1em;display:inline-block;background-color:#0067b859;text-shadow:1px 1px 1px rgba(0,0,0,.3)}.wf-loaded-stage2 .nlbf .nlbf-button{font-family:Mija}.mts{margin-top: 5px !important;}.mbn{margin-bottom: 0 !important;}</style></section><p class="mts mbn"><small class="promo-box__footer mtm block grey"><em>Once a week. Useful tips on <a href="https://www.smashingmagazine.com/the-smashing-newsletter/">front-end &amp; UX</a>. Trusted by 207.000 friendly folks.</em></small></p></div></p>
</div>
</div>
<div class="feature-panel-right-col">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-firechat.svg"
    alt="Feature Panel"
    width="310"
    height="400"
/>

</div>

<p></div>
</aside>
</div></p>

<h2 id="monopolies">Monopolies</h2>

<p>One of the main sources of concern for me is that although more people are online than ever, they are concentrating on fewer and fewer sites. A study <a href="https://www.sciencealert.com/we-re-going-to-fewer-and-fewer-websites-and-that-could-be-a-problem">published in 2021</a> found that <strong>activity is concentrated in a handful of websites</strong>. Think Google, Amazon, Facebook, Instagram, and, more recently, ChatGPT:</p>

<blockquote>“So, while there is still growth in the functions, features, and applications offered on the web, the number of entities providing these functions is shrinking. [...] The authority, influence, and visibility of the top 1,000 global websites (as measured by network centrality or PageRank) is growing every month, at the expense of all other sites.”</blockquote>

<p>Monopolies by nature <strong>reduce variance</strong>, both through their domination of the market and (understandably in fairness) internal preferences for consistency. And, let’s be frank, they have a vested interest in crushing any potential upstarts.</p>

<ul>
<li>“<a href="https://www.smashingmagazine.com/2020/05/readability-algorithms-tools-targets/">Readability Algorithms Should Be Tools, Not Targets</a>”</li>
<li>“<a href="https://www.smashingmagazine.com/2021/01/towards-ad-free-web-diversifying-online-economy/">Towards An Ad-Free Web: Diversifying The Online Economy</a>”</li>
</ul>

<p>Dominant websites often fall victim to what I like to call <strong>Internet Explorer Syndrome</strong>, where their dominance breeds a certain amount of complacency. Why improve your <a href="https://www.smashingmagazine.com/2025/05/what-zen-art-motorcycle-maintenance-teach-web-design/">quality</a> when you’re sitting on 90% market share? No wonder <a href="https://www.standard.co.uk/news/tech/google-search-worse-quality-spam-study-b1133559.html">the likes of Google are getting worse</a>.</p>

<p>The most immediate sign of this is obviously how sites are designed and how they look. A lot of the big players look an awful lot like each other. Even personal websites are built atop third-party website builders. Millions of people wind up using the same handful of templates, and that’s if they have their own website at all. On social media, we are little more than a profile picture and a pithy tagline. The rest is boilerplate.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="728"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png"
			
			sizes="100vw"
			alt="Gratscale minimalist layout example"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/the-grayscale-problem/4-grayscale-minimalist-layout.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Should there be sleek, minimalist, ‘grayscale’ design systems and websites? Absolutely. But there should be colourful, kooky ones too, and if anything, they’re fading away. Do we really want to spend our online lives in the digital equivalent of Levittowns? Even logos are contriving to be less eye-catching. It feels like a matter of time before every major logo is a circle in a pastel colour.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="508"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/the-grayscale-problem/5-levittown.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The arrival of Artificial Intelligence into our everyday lives (and a decent chunk of the digital services we use) has put all of this into overdrive. Amalgamating &mdash; and hallucinating from &mdash; content that was already trending towards a perfect average, it is grayscale in its purest form.</p>

<p>Mix all the colours together, and what do you get? A muddy gray gloop.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="424"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png"
			
			sizes="100vw"
			alt="Colors mixed together into gray lopp"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/the-grayscale-problem/6-mix-colors-muddy-gray-gloop.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I’m not railing against best practice. A lot of conventions have become the standard for good reason. One could just as easily shake their fist at the sky and wonder why all newspapers look the same, or all books. I hope the difference here is clear, though.</p>

<blockquote class="pull-quote">
  <p>
    <a class="pull-quote__link" aria-label="Share on Twitter" href="https://twitter.com/share?text=%0aThe%20web%20is%20a%20flexible%20enough%20domain%20that%20I%20think%20it%20belongs%20in%20the%20realm%20of%20architecture.%20A%20city%20where%20all%20buildings%20look%20alike%20has%20a%20soul-crushing%20quality%20about%20it.%20The%20same%20is%20true,%20I%20think,%20of%20the%20web.%0a&url=https://smashingmagazine.com%2f2025%2f10%2fthe-grayscale-problem%2f">
      
The web is a flexible enough domain that I think it belongs in the realm of architecture. A city where all buildings look alike has a soul-crushing quality about it. The same is true, I think, of the web.

    </a>
  </p>
  <div class="pull-quote__quotation">
    <div class="pull-quote__bg">
      <span class="pull-quote__symbol">“</span></div>
  </div>
</blockquote>

<p>In the Oscar Wilde play <em><a href="https://www.gutenberg.org/files/790/790-h/790-h.htm">Lady Windermere’s Fan</a></em>, a character quips that a cynic <em>“knows the price of everything and the value of nothing.”</em> In fairness, another quips back that a sentimentalist <em>“sees an absurd value in everything, and doesn’t know the market price of any single thing.”</em></p>

<p>The sweet spot is somewhere in between. Structure goes a long way, but life needs a bit of variety too.</p>

<p>So, how do we go about bringing that variety? We probably shouldn’t hold our breath on big players to lead the way. They have the most to lose, after all. Why risk being colourful or dynamic if it impacts the bottom line?</p>

<p>We, the citizens of the web, have more power than we realise. This is the web, remember, a place where if you can imagine it, odds are you can make it. And at zero cost. No materials to buy and ship, no shareholders to appease. A place as flexible &mdash; and limitless &mdash; as the web has no business being boring.</p>

<p>There are plenty of ways, big and small, of keeping this place colourful. Whether our digital footprints are on third-party websites or ones we build ourselves, we needn’t toe the line.</p>

<p><strong>Colour</strong> seems an appropriate place to start. When given the choice, try something audacious rather than safe. The worst that can happen is that it doesn’t work. It’s not like the sunk cost of painting a room; if you don’t like the palette, you simply change the hex codes. The same is true of <a href="https://www.smashingmagazine.com/2023/03/free-fonts-interface-designers/">fonts</a>, <a href="https://www.smashingmagazine.com/2021/08/open-source-icons/">icons</a>, and other building blocks of the web.</p>

<p>As an example, a couple of friends and I listen to and review albums occasionally as a hobby. On the website, the palette of each review page reflects the album artwork:</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="800"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/the-grayscale-problem/8-audioxide-screenshot.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>I couldn’t tell you if reviews ‘perform’ better or worse than if they had a grayscale palette, because I don’t care. I think it’s a lot nicer to look at. And for those wondering, yes, I have tried to make every page meet <a href="https://www.w3.org/WAI/WCAG2AA-Conformance">AA Web Accessibility standards</a>. Vibrant and accessible aren’t mutually exclusive.</p>

<p>Another great way of bringing vibrancy to the web is a <strong>degree of randomisation</strong>. Bruno Simon of <a href="https://threejs-journey.com/">Three Journey</a> and <a href="https://bruno-simon.com/">awesome portfolio</a> fame weaves random generation into a lot of his projects, and the results are gorgeous. What’s more, they feel familiar, natural, because life is full of wildcards.</p>

<figure><a href="https://files.smashing.media/articles/the-grayscale-problem/7-3d-model.gif"><img src="https://files.smashing.media/articles/the-grayscale-problem/7-3d-model.gif" width="800" height="520" alt="3D model" /></a></figure>

<p>This needn’t be in fancy 3D models. You could lightly rotate images to create a more informal, photo album mood, or chuck in the occasional random link in a list of recommended articles, just to shake things up.</p>

<p>In a lot of ways, it boils down to an attitude of just trying stuff out. Make your own font, give the site a sepia filter, and add that easter egg you keep thinking about. Just because someone, somewhere has already done it doesn’t mean you can’t do it your own way. And who knows, maybe your way stumbles onto someplace wholly new.</p>

<p>I’m wary of being too prescriptive. I don’t have the keys to a colourful web. No one person does. A vibrant community is the sum total of its people. What keeps things interesting is individuals trying wacky ideas and putting them out there. Expression for expression’s sake. Experimentation for experimentation’s sake. Tinkering for tinkering’s sake.</p>

<p>As users, there’s also plenty of room to be adventurous and try out <a href="https://openalternative.co/">open source alternatives to the software monopolies</a> that shape so much of today’s Web. Being active in the communities that shape those tools helps to sustain <strong>a more open, collaborative digital world</strong>.</p>

<p>Although there are lessons to be taken from it, we won’t get a more colourful web by idealising the past or pining to get back to the ‘90s. Nor is there any point in resisting new technologies. AI is here; the choice is whether we use it or it uses us. We must have the courage to carry forward what still holds true, drop what doesn’t, and explore new ideas with a spirit of play.</p>

<div class="partners__lead-place"></div>

<p>Here are a few more <em>Smashing</em> articles in that spirit:</p>

<ul>
<li>“<a href="https://www.smashingmagazine.com/2020/11/playfulness-code-supercharge-fun-learning/">Playfulness In Code: Supercharge Your Learning By Having Fun</a>” by Jhey Tompkins</li>
<li>“<a href="https://www.smashingmagazine.com/2025/08/psychology-color-ux-design-digital-products/">The Psychology Of Color In UX And Digital Products</a>” by Rodolpho Henrique</li>
<li>“<a href="https://www.smashingmagazine.com/2020/12/creativity-technology/">Creativity In A World Of Technology: Does It Exist?</a>” By Maggie Mackenzie</li>
<li>“<a href="https://www.smashingmagazine.com/2025/05/what-zen-art-motorcycle-maintenance-teach-web-design/">What Zen And The Art Of Motorcycle Maintenance Can Teach Us About Web Design</a>”</li>
<li>“<a href="https://www.smashingmagazine.com/2025/01/ode-to-side-project-time/">An Ode To Side Project Time</a>”</li>
</ul>

<p>I do think there’s a broader discussion to be had about the extent to which A/B tests, bottom lines, and focus groups seem to dictate much of how the modern web looks and feels. With sites being squeezed tighter and tighter by dwindling advertising revenues, and <a href="https://www.forbes.com/sites/torconstantino/2025/04/14/the-60-problem---how-ai-search-is-draining-your-traffic/">AI answers muscling in on search traffic</a>, the corporate entities behind larger websites can’t justify doing anything other than what is safe and proven, for fear of shrinking their slice of the pie.</p>

<p>Lest we forget, though, most of the web isn’t beholden to those types of pressure. From pet projects to wikis to forums to community news outlets to all manner of other things, there are countless reasons for websites to exist, and they needn’t take design cues from the handful of sites slugging it out at the top.</p>

<p>Connected with this is the dire need for <a href="https://tcg.uis.unesco.org/wp-content/uploads/sites/4/2021/08/Metadata-4.4.2.pdf">digital literacy</a> (PDF) &mdash; ‘the confident and critical use of a full range of digital technologies for information, communication and basic problem-solving in all aspects of life.’ For as long as using third-party platforms is a necessity rather than a choice, the needle’s only going to move so much.</p>

<p>There’s a reason why <a href="https://www.bbc.co.uk/news/technology-67105983">Minecraft is the world’s best-selling game</a>. People are creative. When given the tools &mdash; and the opportunity &mdash; that creativity will manifest in weird and wonderful ways. That game is a lot of things, but gray ain’t one of them.</p>

<p>The web has all of that flexibility and more. It is a <strong>manifestation of imagination</strong>. Imagination trends towards colour, not grayness. It doesn’t always feel like it, but where the internet goes is decided by its citizens. The internet is ours. If we want to, we can make it technicolor.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Andy Clarke</author><title>Smashing Animations Part 5: Building Adaptive SVGs With `&lt;symbol>`, `&lt;use>`, And CSS Media Queries</title><link>https://www.smashingmagazine.com/2025/10/smashing-animations-part-5-building-adaptive-svgs/</link><pubDate>Mon, 06 Oct 2025 13:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/10/smashing-animations-part-5-building-adaptive-svgs/</guid><description>SVGs, they scale, yes, but how else can you make them adapt even better to several screen sizes? Web design pioneer &lt;a href="https://stuffandnonsense.co.uk">Andy Clarke&lt;/a> explains how he builds what he calls “adaptive SVGs” using &lt;code>&amp;lt;symbol&amp;gt;&lt;/code>, &lt;code>&amp;lt;use&amp;gt;&lt;/code>, and CSS Media Queries.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/10/smashing-animations-part-5-building-adaptive-svgs/" />
              <title>Smashing Animations Part 5: Building Adaptive SVGs With `&lt;symbol&gt;`, `&lt;use&gt;`, And CSS Media Queries</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Smashing Animations Part 5: Building Adaptive SVGs With `&lt;symbol&gt;`, `&lt;use&gt;`, And CSS Media Queries</h1>
                  
                    
                    <address>Andy Clarke</address>
                  
                  <time datetime="2025-10-06T13:00:00&#43;00:00" class="op-published">2025-10-06T13:00:00+00:00</time>
                  <time datetime="2025-10-06T13:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>I’ve written quite a lot recently about how I <a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">prepare and optimise</a> SVG code to use as static graphics or in <a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-1-classic-cartoons-inspire-css/">animations</a>. I love working with SVG, but there’s always been something about them that bugs me.</p>

<p>To illustrate how I build adaptive SVGs, I’ve selected an episode of <em>The Quick Draw McGraw Show</em> called “<a href="https://yowpyowp.blogspot.com/2012/06/quick-draw-mcgraw-bow-wow-bandit.html">Bow Wow Bandit</a>,” first broadcast in 1959.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png"
			
			sizes="100vw"
			alt="Bow Wow Bandit illustration"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Quick Draw McGraw Show © Warner Bros. Entertainment Inc. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/1-quick-draw-mcgraw-show.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>In it, Quick Draw McGraw enlists his bloodhound Snuffles to rescue his sidekick Baba Looey. Like most Hanna-Barbera title cards of the period, the artwork was made by Lawrence (Art) Goble.</p>

<div class="refs">
  <ul><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-1-classic-cartoons-inspire-css/">Smashing Animations Part 1: How Classic Cartoons Inspire Modern CSS</a></li><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-2-css-masking-add-extra-dimension/">Smashing Animations Part 2: How CSS Masking Can Add An Extra Dimension</a></li><li><a href="https://www.smashingmagazine.com/2025/05/smashing-animations-part-3-smil-not-dead/">Smashing Animations Part 3: SMIL’s Not Dead Baby, SMIL’s Not Dead</a></li><li><a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">Smashing Animations Part 4: Optimising SVGs</a></li></ul>
</div>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png"
			
			sizes="100vw"
			alt="Quick Draw McGraw character pulling back on a dog leash attached to his bloodhound, Snuffles."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Andy Clarke’s Bow Wow Bandit Toon Title recreation (16:9). (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/2-andy-clarke-bow-wow-bandit-toon-title-recreation.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Let’s say I’ve designed an SVG scene like that one that’s based on Bow Wow Bandit, which has a 16:9 aspect ratio with a <code>viewBox</code> size of 1920×1080. This SVG scales up and down (the clue’s in the name), so it looks sharp when it’s gigantic and when it’s minute.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png"
			
			sizes="100vw"
			alt="16:9 aspect ration vs. 3:4."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Left: 16:9 aspect ratio loses its impact. Right: 3:4 format suits the screen size better. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/3-svgs-aspect-ratio.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But on small screens, the 16:9 aspect ratio (<a href="https://stuffandnonsense.co.uk/toon-titles/quick-draw-3a.html">live demo</a>) might not be the best format, and the image loses its impact. Sometimes, a portrait orientation, like 3:4, would suit the screen size better.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="729"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png"
			
			sizes="100vw"
			alt="Andy Clarke’s Bow Wow Bandit Toon Title recreation (3:4)."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Andy Clarke’s Bow Wow Bandit Toon Title recreation (3:4). (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/4-bow-wow-bandit-toon-title-recreation-portrait.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>But, herein lies the problem, as it’s not easy to reposition internal elements for different screen sizes using just <code>viewBox</code>. That’s because in SVG, internal element positions are locked to the coordinate system from the original <code>viewBox</code>, so you can’t easily change their layout between, say, desktop and mobile. This is a problem because animations and interactivity often rely on element positions, which break when the <code>viewBox</code> changes.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png"
			
			sizes="100vw"
			alt="Left: 16:9 for larger screens. Right: 3:4 for smaller screens."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Left: 16:9 for larger screens. Right: 3:4 for smaller screens. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/5-svg-smaller-larger-screens.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>My challenge was to serve a 1080×1440 version of Bow Wow Bandit to smaller screens and a different one to larger ones. I wanted the position and size of internal elements &mdash; like Quick Draw McGraw and his dawg Snuffles &mdash; to change to best fit these two layouts. To solve this, I experimented with several alternatives.</p>

<p><strong>Note:</strong> Why are we not just using the <code>&lt;picture&gt;</code> with external SVGs? The <a href="https://www.smashingmagazine.com/2014/05/responsive-images-done-right-guide-picture-srcset/"><code>&lt;picture&gt;</code> element</a> is brilliant for responsive images, but it only works with raster formats (like JPEG or WebP) and external SVG files treated as images. That means that you can’t animate or style internal elements using CSS.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="showing-and-hiding-svg">Showing And Hiding SVG</h2>

<p>The most obvious choice was to include two different SVGs in my markup, one for small screens, the other for larger ones, then show or hide them using <a href="https://www.smashingmagazine.com/2018/02/media-queries-responsive-design-2018/">CSS and Media Queries</a>:</p>

<pre><code class="language-svg">&lt;svg id="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;svg id="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;!--... --&gt;
&lt;/svg&gt;


#svg-small { display: block; }
#svg-large { display: none; }

@media (min-width: 64rem) {
  #svg-small { display: none; }
  #svg-mobile { display: block; }
}
</code></pre>

<p>But using this method, both SVG versions are loaded, which, when the graphics are complex, means downloading lots and lots and lots of unnecessary code.</p>

<h2 id="replacing-svgs-using-javascript">Replacing SVGs Using JavaScript</h2>

<p>I thought about using JavaScript to swap in the larger SVG at a specified breakpoint:</p>

<pre><code class="language-javascript">if (window.matchMedia('(min-width: 64rem)').matches) {
  svgContainer.innerHTML = desktopSVG; 
} else {
  svgContainer.innerHTML = mobileSVG;
}
</code></pre>

<p>Leaving aside the fact that JavaScript would now be critical to how the design is displayed, both SVGs would usually be loaded anyway, which adds DOM complexity and unnecessary weight. Plus, maintenance becomes a problem as there are now two versions of the artwork to maintain, doubling the time it would take to update something as small as the shape of Quick Draw’s tail.</p>

<h2 id="the-solution-one-svg-symbol-library-and-multiple-uses">The Solution: One SVG Symbol Library And Multiple Uses</h2>

<p>Remember, my goal is to:</p>

<ul>
<li>Serve one version of Bow Wow Bandit to smaller screens,</li>
<li>Serve a different version to larger screens,</li>
<li>Define my artwork just once (DRY), and</li>
<li>Be able to resize and reposition elements.</li>
</ul>

<p>I don’t read about it enough, but the <code>&lt;symbol&gt;</code> element lets you define reusable SVG elements that can be hidden and reused to improve maintainability and reduce code bloat. They’re like components for SVG: <a href="https://css-tricks.com/svg-symbol-good-choice-icons/">create once and use wherever you need them</a>:</p>

<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" style="display: none;"&gt;
  &lt;symbol id="quick-draw-body" viewBox="0 0 620 700"&gt;
    &lt;g class="quick-draw-body"&gt;[…]&lt;/g&gt;
  &lt;/symbol&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;use href="#quick-draw-body" /&gt;
</code></pre>

<p>A <code>&lt;symbol&gt;</code> is like storing a character in a library. I can reference it as many times as I need, to keep my code consistent and lightweight. Using <code>&lt;use&gt;</code> elements, I can insert the same symbol multiple times, at different positions or sizes, and even in different SVGs.</p>

<p>Each <code>&lt;symbol&gt;</code> must have its own <code>viewBox</code>, which defines its internal coordinate system. That means paying special attention to how SVG elements are exported from apps like Sketch.</p>

<div class="partners__lead-place"></div>

<h2 id="exporting-for-individual-viewboxes">Exporting For Individual Viewboxes</h2>

<p>I wrote before about <a href="https://www.smashingmagazine.com/2025/06/smashing-animations-part-4-optimising-svgs/">how I export elements</a> in layers to make working with them easier. That process is a little different when creating symbols.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png"
			
			sizes="100vw"
			alt=""
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      My usual process of exporting elements from Sketch. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/6-exporting-elements-from-sketch.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Ordinarily, I would export all my elements using the same <code>viewBox</code>size. But when I’m creating a <code>symbol</code>, I need it to have its own specific <code>viewBox</code>.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png"
			
			sizes="100vw"
			alt="Exporting elements from Sketch as individual SVG files."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Exporting elements from Sketch as individual SVG files. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/7-exporting-elements-sketch-individual-svgs-files.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>So I export each element as an individually sized SVG, which gives me the dimensions I need to convert its content into a <code>symbol</code>. Let’s take the SVG of Quick Draw McGraw’s hat, which has a <code>viewBox</code> size of 294×182:</p>

<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 294 182"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;
</code></pre>

<p>I swap the SVG tags for <code>&lt;symbol&gt;</code> and add its artwork to my SVG library:</p>

<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" style="display: none;"&gt;
  &lt;symbol id="quick-draw-hat" viewBox="0 0 294 182"&gt;
    &lt;g class="quick-draw-hat"&gt;[…]&lt;/g&gt;
  &lt;/symbol&gt;
&lt;/svg&gt;
</code></pre>

<p>Then, I repeat the process for all the remaining elements in my artwork. Now, if I ever need to update any of my symbols, the changes will be automatically applied to every instance it’s used.</p>

<h2 id="using-a-symbol-in-multiple-svgs">Using A <code>&lt;symbol&gt;</code> In Multiple SVGs</h2>

<p>I wanted my elements to appear in both versions of Bow Wow Bandit, one arrangement for smaller screens and an alternative arrangement for larger ones. So, I create both SVGs:</p>

<pre><code class="language-svg">&lt;svg class="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;

&lt;svg class="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;!-- ... --&gt;
&lt;/svg&gt;
</code></pre>

<p>…and insert links to my symbols in both:</p>

<pre><code class="language-svg">&lt;svg class="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;use href="#quick-draw-hat" /&gt;
&lt;/svg&gt;

&lt;svg class="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;use href="#quick-draw-hat" /&gt;
&lt;/svg&gt;
</code></pre>

<h2 id="positioning-symbols">Positioning Symbols</h2>

<p>Once I’ve placed symbols into my layout using <code>&lt;use&gt;</code>, my next step is to position them, which is especially important if I want alternative layouts for different screen sizes. Symbols behave like <code>&lt;g&gt;</code> groups, so I can scale and move them using attributes like <code>width</code>, <code>height</code>, and <code>transform</code>:</p>

<div class="break-out">
<pre><code class="language-svg">&lt;svg class="svg-small" viewBox="0 0 1080 1440"&gt;
  &lt;use href="#quick-draw-hat" width="294" height="182" transform="translate(-30,610)"/&gt;
&lt;/svg&gt;

&lt;svg class="svg-large" viewBox="0 0 1920 1080"&gt;
  &lt;use href="#quick-draw-hat" width="294" height="182" transform="translate(350,270)"/&gt;
&lt;/svg&gt;
</code></pre>
</div>

<p>I can place each <code>&lt;use&gt;</code> element independently using <code>transform</code>. This is powerful because rather than repositioning elements inside my SVGs, I move the <code>&lt;use&gt;</code> references. My internal layout stays clean, and the file size remains small because I’m not duplicating artwork. A browser only loads it once, which reduces bandwidth and speeds up page rendering. And because I’m always referencing the same <code>symbol</code>, their appearance stays consistent, whatever the screen size.</p>

<h2 id="animating-use-elements">Animating <code>&lt;use&gt;</code> Elements</h2>

<p>Here’s where things got tricky. I wanted to animate parts of my characters &mdash; like Quick Draw’s hat tilting and his legs kicking. But when I added CSS animations targeting internal elements inside a <code>&lt;symbol&gt;</code>, nothing happened.</p>

<p><strong>Tip:</strong> You can animate the <code>&lt;use&gt;</code> element itself, but not elements inside the <code>&lt;symbol&gt;</code>. If you want individual parts to move, make them their own symbols and animate each <code>&lt;use&gt;</code>.</p>

<p>Turns out, you can’t style or animate a <code>&lt;symbol&gt;</code>, because <code>&lt;use&gt;</code> creates shadow DOM clones that aren’t easily targetable. So, I had to get sneaky. Inside each <code>&lt;symbol&gt;</code> in my library SVG, I added a <code>&lt;g&gt;</code> element around the part I wanted to animate:</p>

<pre><code class="language-svg">&lt;symbol id="quick-draw-hat" viewBox="0 0 294 182"&gt;
  &lt;g class="quick-draw-hat"&gt;
    &lt;!-- ... --&gt;
  &lt;/g&gt;
&lt;/symbol&gt;
</code></pre>

<p>…and animated it using an attribute substring selector, targeting the <code>href</code> attribute of the <code>use</code> element:</p>

<pre><code class="language-css">use[href="#quick-draw-hat"] {
  animation-delay: 0.5s;
  animation-direction: alternate;
  animation-duration: 1s;
  animation-iteration-count: infinite;
  animation-name: hat-rock;
  animation-timing-function: ease-in-out;
  transform-origin: center bottom;
}

@keyframes hat-rock {
from { transform: rotate(-2deg); }
to   { transform: rotate(2deg); } }
</code></pre>

<div class="partners__lead-place"></div>

<h2 id="media-queries-for-display-control">Media Queries For Display Control</h2>

<p>Once I’ve created my two visible SVGs &mdash; one for small screens and one for larger ones &mdash; the final step is deciding which version to show at which screen size. I use CSS Media Queries to hide one SVG and show the other. I start by showing the small-screen SVG by default:</p>

<pre><code class="language-css">.svg-small { display: block; }
.svg-large { display: none; }
</code></pre>

<p>Then I use a <code>min-width</code> media query to switch to the large-screen SVG at <code>64rem</code> and above:</p>

<pre><code class="language-css">@media (min-width: 64rem) {
  .svg-small { display: none; }
  .svg-large { display: block; }
}
</code></pre>

<p>This ensures there’s only ever one SVG visible at a time, keeping my layout simple and the DOM free from unnecessary clutter. And because both visible SVGs reference the same hidden <code>&lt;symbol&gt;</code> library, the browser only downloads the artwork once, regardless of how many <code>&lt;use&gt;</code> elements appear across the two layouts.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="450"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png"
			
			sizes="100vw"
			alt="The final adaptive SVG."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      View the final adaptive SVG on my <a href='https://stuffandnonsense.co.uk/toon-titles/quick-draw-3.html'>Toon Titles website</a>. (<a href='https://files.smashing.media/articles/smashing-animations-part-5-building-adaptive-svgs/8-final-adaptive-svg.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>By combining <code>&lt;symbol&gt;</code>, <code>&lt;use&gt;</code>, CSS Media Queries, and specific transforms, I can build <strong>adaptive SVGs</strong> that reposition their elements without duplicating content, loading extra assets, or relying on JavaScript. I need to define each graphic only once in a hidden symbol library. Then I can reuse those graphics, as needed, inside several visible SVGs. With CSS doing the layout switching, the <strong>result is fast and flexible</strong>.</p>

<p>It’s a reminder that some of the most powerful techniques on the web don’t need big frameworks or complex tooling &mdash; just a bit of SVG know-how and a clever use of the basics.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(gg, yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Yegor Gilyov</author><title>Intent Prototyping: A Practical Guide To Building With Clarity (Part 2)</title><link>https://www.smashingmagazine.com/2025/10/intent-prototyping-practical-guide-building-clarity/</link><pubDate>Fri, 03 Oct 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/10/intent-prototyping-practical-guide-building-clarity/</guid><description>Ready to move beyond static mockups? Here is a practical, step-by-step guide to Intent Prototyping &amp;mdash; a disciplined method that uses AI to turn your design intent (UI sketches, conceptual models, and user flows) directly into a live prototype, making it your primary canvas for ideation.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/10/intent-prototyping-practical-guide-building-clarity/" />
              <title>Intent Prototyping: A Practical Guide To Building With Clarity (Part 2)</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>Intent Prototyping: A Practical Guide To Building With Clarity (Part 2)</h1>
                  
                    
                    <address>Yegor Gilyov</address>
                  
                  <time datetime="2025-10-03T10:00:00&#43;00:00" class="op-published">2025-10-03T10:00:00+00:00</time>
                  <time datetime="2025-10-03T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>In <strong><a href="https://www.smashingmagazine.com/2025/09/intent-prototyping-pure-vibe-coding-enterprise-ux/">Part 1</a></strong> of this series, we explored the “lopsided horse” problem born from mockup-centric design and demonstrated how the seductive promise of vibe coding often leads to structural flaws. The main question remains:</p>

<blockquote>How might we close the gap between our design intent and a live prototype, so that we can iterate on real functionality from day one, without getting caught in the ambiguity trap?</blockquote>

<p>In other words, we need a way to build prototypes that are both fast to create and founded on a clear, unambiguous blueprint.</p>

<p>The answer is a more disciplined process I call <strong>Intent Prototyping</strong> (kudos to Marco Kotrotsos, who coined <a href="https://kotrotsos.medium.com/intent-oriented-programming-bridging-human-thought-and-ai-machine-execution-3a92373cc1b6">Intent-Oriented Programming</a>). This method embraces the power of AI-assisted coding but rejects ambiguity, putting the designer’s explicit <em>intent</em> at the very center of the process. It receives a holistic expression of <em>intent</em> (sketches for screen layouts, conceptual model description, boxes-and-arrows for user flows) and uses it to generate a live, testable prototype.</p>














<figure class="
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="491"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg"
			
			sizes="100vw"
			alt="Diagram showing sketches, a conceptual model, and user flows as inputs to Intent Prototyping, which outputs a live prototype."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The Intent Prototyping workflow. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/1-intent-prototyping.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>This method solves the concerns we’ve discussed in Part 1 in the best way possible:</p>

<ul>
<li><strong>Unlike static mockups,</strong> the prototype is fully interactive and can be easily populated with a large amount of realistic data. This lets us test the system’s underlying logic as well as its surface.</li>
<li><strong>Unlike a vibe-coded prototype</strong>, it is built from a stable, unambiguous specification. This prevents the conceptual model failures and design debt that happen when things are unclear. The engineering team doesn’t need to reverse-engineer a black box or become “code archaeologists” to guess at the designer’s vision, as they receive not only a live prototype but also a clearly documented design intent behind it.</li>
</ul>

<p>This combination makes the method especially suited for designing complex enterprise applications. It allows us to test the system’s most critical point of failure, its underlying structure, at a speed and flexibility that was previously impossible. Furthermore, the process is built for iteration. You can explore as many directions as you want simply by changing the intent and evolving the design based on what you learn from user testing.</p>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h2 id="my-workflow">My Workflow</h2>

<p>To illustrate this process in action, let’s walk through a case study. It’s the very same example I’ve used to illustrate the vibe coding trap: a simple tool to track tests to validate product ideas. You can find the complete project, including all the source code and documentation files discussed below, in this <a href="https://github.com/YegorGilyov/reality-check">GitHub repository</a>.</p>

<h3 id="step-1-expressing-an-intent">Step 1: Expressing An Intent</h3>

<p>Imagine we’ve already done proper research, and having mused on the defined problem, I begin to form a vague idea of what the solution might look like. I need to capture this idea immediately, so I quickly sketch it out:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="583"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png"
			
			sizes="100vw"
			alt="A rough sketch of screens to manage product ideas and reality checks."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      A low-fidelity sketch of the initial idea. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/2-low-fidelity-sketch-initial-idea.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>In this example, I used Excalidraw, but the tool doesn’t really matter. Note that we deliberately keep it rough, as visual details are not something we need to focus on at this stage. And we are not going to be stuck here: we want to make a leap from this initial sketch directly to a live prototype that we can put in front of potential users. Polishing those sketches would not bring us any closer to achieving our goal.</p>

<p>What we need to move forward is to add to those sketches just enough details so that they may serve as a sufficient input for a junior frontend developer (or, in our case, an AI assistant). This requires explaining the following:</p>

<ul>
<li>Navigational paths (clicking here takes you to).</li>
<li>Interaction details that can’t be shown in a static picture (e.g., non-scrollable areas, adaptive layout, drag-and-drop behavior).</li>
<li>What parts might make sense to build as reusable components.</li>
<li>Which components from the design system (I’m using <a href="https://ant.design/">Ant Design Library</a>) should be used.</li>
<li>Any other comments that help understand how this thing should work (while sketches illustrate how it should look).</li>
</ul>

<p>Having added all those details, we end up with such an annotated sketch:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="399"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png"
			
			sizes="100vw"
			alt="The initial sketch with annotations specifying components, navigation, and interaction details."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The sketch annotated with details. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/3-sketch-annotated-details.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>As you see, this sketch covers both the Visualization and Flow aspects. You may ask, what about the Conceptual Model? Without that part, the expression of our <em>intent</em> will not be complete. One way would be to add it somewhere in the margins of the sketch (for example, as a UML Class Diagram), and I would do so in the case of a more complex application, where the model cannot be simply derived from the UI. But in our case, we can save effort and ask an LLM to generate a comprehensive description of the conceptual model based on the sketch.</p>

<p>For tasks of this sort, the LLM of my choice is Gemini 2.5 Pro. What is important is that this is a multimodal model that can accept not only text but also images as input (GPT-5 and Claude-4 also fit that criteria). I use Google AI Studio, as it gives me enough control and visibility into what’s happening:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="579"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png"
			
			sizes="100vw"
			alt="Screenshot of Google AI Studio with an annotated sketch as input."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Generating a conceptual model from the sketch using Google AI Studio. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/4-google-ai-studio.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p><strong>Note</strong>: <em>All the prompts that I use here and below can be found in the <a href="#appendices">Appendices</a>. The prompts are not custom-tailored to any particular project; they are supposed to be reused as they are.</em></p>

<p>As a result, Gemini gives us a description and the following diagram:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="480"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg"
			
			sizes="100vw"
			alt="UML class diagram showing two connected entities: “ProductIdea” and “RealityCheck”."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      UML class diagram. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/5-uml-class.jpg'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The diagram might look technical, but I believe that a clear understanding of all objects, their attributes, and relationships between them is key to good design. That’s why I consider the Conceptual Model to be an essential part of expressing <em>intent</em>, along with the Flow and Visualization.</p>

<p>As a result of this step, our <em>intent</em> is fully expressed in two files: <code>Sketch.png</code> and <code>Model.md</code>. This will be our durable source of truth.</p>

<h3 id="step-2-preparing-a-spec-and-a-plan">Step 2: Preparing A Spec And A Plan</h3>

<p>The purpose of this step is to create a comprehensive technical specification and a step-by-step plan. Most of the work here is done by AI; you just need to keep an eye on it.</p>

<p>I separate the Data Access Layer and the UI layer, and create specifications for them using two different prompts (see <a href="#appendices">Appendices 2 and 3</a>). The output of the first prompt (the Data Access Layer spec) serves as an input for the second one. Note that, as an additional input, we give the guidelines tailored for prototyping needs (see <a href="#appendices">Appendices 8, 9, and 10</a>). They are not specific to this project. The technical approach encoded in those guidelines is out of the scope of this article.</p>

<p>As a result, Gemini provides us with content for <code>DAL.md</code> and <code>UI.md</code>. Although in most cases this result is quite reliable enough, you might want to scrutinize the output. You don’t need to be a real programmer to make sense of it, but some level of programming literacy would be really helpful. However, even if you don’t have such skills, don’t get discouraged. The good news is that if you don’t understand something, you always know who to ask. Do it in Google AI Studio before refreshing the context window. If you believe you’ve spotted a problem, let Gemini know, and it will either fix it or explain why the suggested approach is actually better.</p>

<p>It’s important to remember that by their nature, <strong>LLMs are not deterministic</strong> and, to put it simply, can be forgetful about small details, especially when it comes to details in sketches. Fortunately, you don’t have to be an expert to notice that the “Delete” button, which is in the upper right corner of the sketch, is not mentioned in the spec.</p>

<p>Don’t get me wrong: Gemini does a stellar job most of the time, but there are still times when it slips up. Just let it know about the problems you’ve spotted, and everything will be fixed.</p>

<p>Once we have <code>Sketch.png</code>, <code>Model.md</code>, <code>DAL.md</code>, <code>UI.md</code>, and we have reviewed the specs, we can grab a coffee. We deserve it: our technical design documentation is complete. It will serve as a stable foundation for building the actual thing, without deviating from our original intent, and ensuring that all components fit together perfectly, and all layers are stacked correctly.</p>

<p>One last thing we can do before moving on to the next steps is to prepare a step-by-step plan. We split that plan into two parts: one for the Data Access Layer and another for the UI. You can find prompts I use to create such a plan in <a href="#appendices">Appendices 4 and 5</a>.</p>

<h3 id="step-3-executing-the-plan">Step 3: Executing The Plan</h3>

<p>To start building the actual thing, we need to switch to another category of AI tools. Up until this point, we have relied on Generative AI. It excels at creating new content (in our case, specifications and plans) based on a single prompt. I’m using Google Gemini 2.5 Pro in Google AI Studio, but other similar tools may also fit such one-off tasks: ChatGPT, Claude, Grok, and DeepSeek.</p>

<p>However, at this step, this wouldn’t be enough. Building a prototype based on specs and according to a plan requires an AI that can read context from multiple files, execute a sequence of tasks, and maintain coherence. A simple generative AI can’t do this. It would be like asking a person to build a house by only ever showing them a single brick. What we need is an agentic AI that can be given the full house blueprint and a project plan, and then get to work building the foundation, framing the walls, and adding the roof in the correct sequence.</p>

<p>My coding agent of choice is Google Gemini CLI, simply because Gemini 2.5 Pro serves me well, and I don’t think we need any middleman like Cursor or Windsurf (which would use Claude, Gemini, or GPT under the hood anyway). If I used Claude, my choice would be Claude Code, but since I’m sticking with Gemini, Gemini CLI it is. But if you prefer Cursor or Windsurf, I believe you can apply the same process with your favourite tool.</p>

<p>Before tasking the agent, we need to create a basic template for our React application. I won’t go into this here. You can find plenty of tutorials on how to scaffold an empty React project using Vite.</p>

<p>Then we put all our files into that project:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="666"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png"
			
			sizes="100vw"
			alt="A file directory showing the docs folder containing DAL.md, Model.md, Sketch.png, and UI.md."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Project structure with design intent and spec files. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/6-project-structure-design-intent-spec-files.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Once the basic template with all our files is ready, we open Terminal, go to the folder where our project resides, and type “gemini”:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="419"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png"
			
			sizes="100vw"
			alt="Screenshot of a terminal showing the Gemini CLI."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Gemini CLI. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/7-gemini.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>And we send the prompt to build the Data Access Layer (see <a href="#appendices">Appendix 6</a>). That prompt implies step-by-step execution, so upon completion of each step, I send the following:</p>

<div class="break-out">
<pre><code class="language-markdown">Thank you! Now, please move to the next task.
Remember that you must not make assumptions based on common patterns; always verify them with the actual data from the spec. 
After each task, stop so that I can test it. Don’t move to the next task before I tell you to do so.
</code></pre>
</div>

<p>As the last task in the plan, the agent builds a special page where we can test all the capabilities of our Data Access Layer, so that we can manually test it. It may look like this:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="572"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png"
			
			sizes="100vw"
			alt="A basic webpage with forms and buttons to test the Data Access Layer’s CRUD functions."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The AI-generated test page for the Data Access Layer. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/8-ai-generated-test-page-data-access-layer.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>It doesn’t look fancy, to say the least, but it allows us to ensure that the Data Access Layer works correctly before we proceed with building the final UI.</p>

<p>And finally, we clear the Gemini CLI context window to give it more headspace and send the prompt to build the UI (see <a href="#appendices">Appendix 7</a>). This prompt also implies step-by-step execution. Upon completion of each step, we test how it works and how it looks, following the “Manual Testing Plan” from <code>UI-plan.md</code>. I have to say that despite the fact that the sketch has been uploaded to the model context and, in general, Gemini tries to follow it, attention to visual detail is not one of its strengths (yet). Usually, a few additional nudges are needed at each step to improve the look and feel:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="320"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png"
			
			sizes="100vw"
			alt="A before-and-after comparison showing the UI&#39;s visual improvement."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Refining the AI-generated UI to match the sketch. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/9-refined-ai-generated-ui.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>Once I’m happy with the result of a step, I ask Gemini to move on:</p>

<div class="break-out">
<pre><code class="language-markdown">Thank you! Now, please move to the next task.
Make sure you build the UI according to the sketch; this is very important. Remember that you must not make assumptions based on common patterns; always verify them with the actual data from the spec and the sketch.  
After each task, stop so that I can test it. Don’t move to the next task before I tell you to do so.
</code></pre>
</div>

<p>Before long, the result looks like this, and in every detail it works exactly as we <em>intended</em>:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="486"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png"
			
			sizes="100vw"
			alt="Screenshots of the final, polished application UI."
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      The final interactive prototype. (<a href='https://files.smashing.media/articles/intent-prototyping-practical-guide-building-clarity/10-final-interactive-prototype.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The prototype is up and running and looking nice. Does it mean that we are done with our work? Surely not, the most fascinating part is just beginning.</p>

<div class="partners__lead-place"></div>

<h3 id="step-4-learning-and-iterating">Step 4: Learning And Iterating</h3>

<p>It’s time to put the prototype in front of potential users and learn more about whether this solution relieves their pain or not.</p>

<p>And as soon as we learn something new, we iterate. We adjust or extend the sketches and the conceptual model, based on that new input, we update the specifications, create plans to make changes according to the new specifications, and execute those plans. In other words, for every iteration, we repeat the steps I’ve just walked you through.</p>

<h3 id="is-this-workflow-too-heavy">Is This Workflow Too Heavy?</h3>

<p>This four-step workflow may create an impression of a somewhat heavy process that requires too much thinking upfront and doesn’t really facilitate creativity. But before jumping to that conclusion, consider the following:</p>

<ul>
<li>In practice, only the first step requires real effort, as well as learning in the last step. AI does most of the work in between; you just need to keep an eye on it.</li>
<li>Individual iterations don’t need to be big. You can start with a <a href="https://wiki.c2.com/?WalkingSkeleton">Walking Skeleton</a>: the bare minimum implementation of the thing you have in mind, and add more substance in subsequent iterations. You are welcome to change your mind about the overall direction in between iterations.</li>
<li>And last but not least, maybe the idea of “think before you do” is not something you need to run away from. A clear and unambiguous statement of intent can prevent many unnecessary mistakes and save a lot of effort down the road.</li>
</ul>

<h2 id="intent-prototyping-vs-other-methods">Intent Prototyping Vs. Other Methods</h2>

<p>There is no method that fits all situations, and Intent Prototyping is not an exception. Like any specialized tool, it has a specific purpose. The most effective teams are not those who master a single method, but those who understand which approach to use to mitigate the most significant risk at each stage. The table below gives you a way to make this choice clearer. It puts Intent Prototyping next to other common methods and tools and explains each one in terms of the primary goal it helps achieve and the specific risks it is best suited to mitigate.</p>

<table class="tablesaw break-out" style="grid-column: 3 / 18; font-size: 13pt;">
    <thead>
        <tr>
            <th>Method/Tool</th>
            <th>Goal</th>
            <th>Risks it is best suited to mitigate</th>
            <th width="300">Examples</th>
            <th>Why</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Intent Prototyping</td>
            <td>To rapidly iterate on the fundamental architecture of a data-heavy application with a complex conceptual model, sophisticated business logic, and non-linear user flows.</td>
            <td>Building a system with a flawed or incoherent conceptual model, leading to critical bugs and costly refactoring.</td>
            <td><ul><li>A CRM (Customer Relationship Management system).</li><li>A Resource Management Tool.</li><li>A No-Code Integration Platform (admin’s UI).</li></ul></td>
            <td>It enforces conceptual clarity. This not only de-risks the core structure but also produces a clear, documented blueprint that serves as a superior specification for the engineering handoff.</td>
        </tr>
        <tr>
            <td>Vibe Coding (Conversational)</td>
            <td>To rapidly explore interactive ideas through improvisation.</td>
            <td>Losing momentum because of analysis paralysis.</td>
            <td><ul><li>An interactive data table with live sorting/filtering.</li><li>A novel navigation concept.</li><li>A proof-of-concept for a single, complex component.</li></ul></td>
            <td>It has the smallest loop between an idea conveyed in natural language and an interactive outcome.</td>
        </tr>
        <tr>
            <td>Axure</td>
            <td>To test complicated conditional logic within a specific user journey, without having to worry about how the whole system works.</td>
            <td>Designing flows that break when users don’t follow the “happy path.”</td>
            <td><ul><li>A multi-step e-commerce checkout.</li><li>A software configuration wizard.</li><li>A dynamic form with dependent fields.</li></ul></td>
            <td>It’s made to create complex <code>if-then</code> logic and manage variables visually. This lets you test complicated paths and edge cases in a user journey without writing any code.</td>
        </tr>
        <tr>
            <td>Figma</td>
            <td>To make sure that the user interface looks good, aligns with the brand, and has a clear information architecture.</td>
            <td>Making a product that looks bad, doesn't fit with the brand, or has a layout that is hard to understand.</td>
            <td><ul><li>A marketing landing page.</li><li>A user onboarding flow.</li><li>Presenting a new visual identity.</li></ul></td>
            <td>It excels at high-fidelity visual design and provides simple, fast tools for linking static screens.</td>
        </tr>
        <tr>
            <td>ProtoPie, Framer</td>
            <td>To make high-fidelity micro-interactions feel just right.</td>
            <td>Shipping an application that feels cumbersome and unpleasant to use because of poorly executed interactions.</td>
            <td><ul><li>A custom pull-to-refresh animation.</li><li>A fluid drag-and-drop interface.</li><li>An animated chart or data visualization.</li></ul></td>
            <td>These tools let you manipulate animation timelines, physics, and device sensor inputs in great detail. Designers can carefully work on and test the small things that make an interface feel really polished and fun to use.</td>
        </tr>
        <tr>
            <td>Low-code / No-code Tools (e.g., Bubble, Retool)</td>
            <td>To create a working, data-driven app as quickly as possible.</td>
            <td>The application will never be built because traditional development is too expensive.</td>
            <td><ul><li>An internal inventory tracker.</li><li>A customer support dashboard.</li><li>A simple directory website.</li></ul></td>
            <td>They put a UI builder, a database, and hosting all in one place. The goal is not merely to make a prototype of an idea, but to make and release an actual, working product. This is the last step for many internal tools or MVPs.</td>
        </tr>
    </tbody>
</table>

<p><br /></p>

<p>The key takeaway is that each method is a <strong>specialized tool for mitigating a specific type of risk</strong>. For example, Figma de-risks the visual presentation. ProtoPie de-risks the feel of an interaction. Intent Prototyping is in a unique position to tackle the most foundational risk in complex applications: building on a flawed or incoherent conceptual model.</p>

<div class="partners__lead-place"></div>

<h2 id="bringing-it-all-together">Bringing It All Together</h2>

<p>The era of the “lopsided horse” design, sleek on the surface but structurally unsound, is a direct result of the trade-off between fidelity and flexibility. This trade-off has led to a process filled with redundant effort and misplaced focus. Intent Prototyping, powered by modern AI, eliminates that conflict. It’s not just a shortcut to building faster &mdash; it’s a <strong>fundamental shift in how we design</strong>. By putting a clear, unambiguous <em>intent</em> at the heart of the process, it lets us get rid of the redundant work and focus on architecting a sound and robust system.</p>

<p>There are three major benefits to this renewed focus. First, by going straight to live, interactive prototypes, we shift our validation efforts from the surface to the deep, testing the system’s actual logic with users from day one. Second, the very act of documenting the design <em>intent</em> makes us clear about our ideas, ensuring that we fully understand the system’s underlying logic. Finally, this documented <em>intent</em> becomes a durable source of truth, eliminating the ambiguous handoffs and the redundant, error-prone work of having engineers reverse-engineer a designer’s vision from a black box.</p>

<p>Ultimately, Intent Prototyping changes the object of our work. It allows us to move beyond creating <strong>pictures of a product</strong> and empowers us to become architects of <strong>blueprints for a system</strong>. With the help of AI, we can finally make the live prototype the primary canvas for ideation, not just a high-effort afterthought.</p>

<h3 id="appendices">Appendices</h3>

<p>You can find the full <strong>Intent Prototyping Starter Kit</strong>, which includes all those prompts and guidelines, as well as the example from this article and a minimal boilerplate project, in this <a href="https://github.com/YegorGilyov/intent-prototyping-starter-kit">GitHub repository</a>.</p>

<div class="js-table-accordion accordion book__toc" id="TOC" aria-multiselectable="true">
    <dl class="accordion-list" style="margin-bottom: 1em" data-handler="Accordion">
          <dt tabindex="0" class="accordion-item" id="accordion-item-0" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 1: Sketch to UML Class Diagram
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-0" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">You are an expert Senior Software Architect specializing in Domain-Driven Design. You are tasked with defining a conceptual model for an app based on information from a UI sketch.

&#35;&#35; Workflow

Follow these steps precisely:

&#42;&#42;Step 1:&#42;&#42; Analyze the sketch carefully. There should be no ambiguity about what we are building.

&#42;&#42;Step 2:&#42;&#42; Generate the conceptual model description in the Mermaid format using a UML class diagram.

&#35;&#35; Ground Rules

- Every entity must have the following attributes:
    - `id` (string)
    - `createdAt` (string, ISO 8601 format)
    - `updatedAt` (string, ISO 8601 format)
- Include all attributes shown in the UI: If a piece of data is visually represented as a field for an entity, include it in the model, even if it's calculated from other attributes.
- Do not add any speculative entities, attributes, or relationships ("just in case"). The model should serve the current sketch's requirements only. 
- Pay special attention to cardinality definitions (e.g., if a relationship is optional on both sides, it cannot be `"1" -- "0..*"`, it must be `"0..1" -- "0..*"`).
- Use only valid syntax in the Mermaid diagram.
- Do not include enumerations in the Mermaid diagram.
- Add comments explaining the purpose of every entity, attribute, and relationship, and their expected behavior (not as a part of the diagram, in the Markdown file).

&#35;&#35; Naming Conventions

- Names should reveal intent and purpose.
- Use PascalCase for entity names.
- Use camelCase for attributes and relationships.
- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).

&#35;&#35; Final Instructions

- &#42;&#42;No Assumptions:** Base every detail on visual evidence in the sketch, not on common design patterns. 
- &#42;*Double-Check:** After composing the entire document, read through it to ensure the hierarchy is logical, the descriptions are unambiguous, and the formatting is consistent. The final document should be a self-contained, comprehensive specification. 
- &#42;&#42;Do not add redundant empty lines between items.&#42;&#42; 

Your final output should be the complete, raw markdown content for `Model.md`.
</code></pre>
</div>
</p>
             </div>
         </dd>
          <dt tabindex="0" class="accordion-item" id="accordion-item-1" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 2: Sketch to DAL Spec
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-1" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">You are an expert Senior Frontend Developer specializing in React, TypeScript, and Zustand. You are tasked with creating a comprehensive technical specification for the development team in a structured markdown document, based on a UI sketch and a conceptual model description. 

&#35;&#35; Workflow

Follow these steps precisely:

&#42;&#42;Step 1:&#42;&#42; Analyze the documentation carefully:

- `Model.md`: the conceptual model
- `Sketch.png`: the UI sketch

There should be no ambiguity about what we are building.

&#42;&#42;Step 2:&#42;&#42; Check out the guidelines:

- `TS-guidelines.md`: TypeScript Best Practices
- `React-guidelines.md`: React Best Practices
- `Zustand-guidelines.md`: Zustand Best Practices

&#42;&#42;Step 3:&#42;&#42; Create a Markdown specification for the stores and entity-specific hook that implements all the logic and provides all required operations.

---

&#35;&#35; Markdown Output Structure

Use this template for the entire document.

```markdown

&#35; Data Access Layer Specification

This document outlines the specification for the data access layer of the application, following the principles defined in `docs/guidelines/Zustand-guidelines.md`.

&#35;&#35; 1. Type Definitions

Location: `src/types/entities.ts`

&#35;&#35;&#35; 1.1. `BaseEntity`

A shared interface that all entities should extend.

[TypeScript interface definition]

&#35;&#35;&#35; 1.2. `[Entity Name]`

The interface for the [Entity Name] entity.

[TypeScript interface definition]

&#35;&#35; 2. Zustand Stores

&#35;&#35;&#35; 2.1. Store for `[Entity Name]`

&#42;&#42;Location:&#42;&#42; `src/stores/[Entity Name (plural)].ts`

The Zustand store will manage the state of all [Entity Name] items.

&#42;&#42;Store State (`[Entity Name]State`):&#42;&#42;

[TypeScript interface definition]

&#42;&#42;Store Implementation (`use[Entity Name]Store`):&#42;&#42;

- The store will be created using `create&lt;[Entity Name]State&gt;()(...)`.
- It will use the `persist` middleware from `zustand/middleware` to save state to `localStorage`. The persistence key will be `[entity-storage-key]`.
- `[Entity Name (plural, camelCase)]` will be a dictionary (`Record&lt;string, [Entity]&gt;`) for O(1) access.

&#42;&#42;Actions:&#42;&#42;

- &#42;&#42;`add[Entity Name]`&#42;&#42;:  
    [Define the operation behavior based on entity requirements]
- &#42;&#42;`update[Entity Name]`&#42;&#42;:  
    [Define the operation behavior based on entity requirements]
- &#42;&#42;`remove[Entity Name]`&#42;&#42;:  
    [Define the operation behavior based on entity requirements]
- &#42;&#42;`doSomethingElseWith[Entity Name]`&#42;&#42;:  
    [Define the operation behavior based on entity requirements]
    
&#35;&#35; 3. Custom Hooks

&#35;&#35;&#35; 3.1. `use[Entity Name (plural)]`

&#42;&#42;Location:&#42;&#42; `src/hooks/use[Entity Name (plural)].ts`

The hook will be the primary interface for UI components to interact with [Entity Name] data.

&#42;&#42;Hook Return Value:&#42;&#42;

[TypeScript interface definition]

&#42;&#42;Hook Implementation:&#42;&#42;

[List all properties and methods returned by this hook, and briefly explain the logic behind them, including data transformations, memoization. Do not write the actual code here.]

```

--- 

&#35;&#35; Final Instructions

- &#42;&#42;No Assumptions:&#42;&#42; Base every detail in the specification on the conceptual model or visual evidence in the sketch, not on common design patterns. 
- &#42;&#42;Double-Check:&#42;&#42; After composing the entire document, read through it to ensure the hierarchy is logical, the descriptions are unambiguous, and the formatting is consistent. The final document should be a self-contained, comprehensive specification. 
- &#42;&#42;Do not add redundant empty lines between items.&#42;&#42; 

Your final output should be the complete, raw markdown content for `DAL.md`.
</code></pre>
</div>
</p>
             </div>
         </dd>
          <dt tabindex="0" class="accordion-item" id="accordion-item-2" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 3: Sketch to UI Spec
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-2" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">You are an expert Senior Frontend Developer specializing in React, TypeScript, and the Ant Design library. You are tasked with creating a comprehensive technical specification by translating a UI sketch into a structured markdown document for the development team.

&#35;&#35; Workflow

Follow these steps precisely:

&#42;&#42;Step 1:&#42;&#42; Analyze the documentation carefully: 

- `Sketch.png`: the UI sketch
  - Note that red lines, red arrows, and red text within the sketch are annotations for you and should not be part of the final UI design. They provide hints and clarification. Never translate them to UI elements directly.
- `Model.md`: the conceptual model
- `DAL.md`: the Data Access Layer spec

There should be no ambiguity about what we are building.

&#42;&#42;Step 2:&#42;&#42; Check out the guidelines:

- `TS-guidelines.md`: TypeScript Best Practices
- `React-guidelines.md`: React Best Practices

&#42;&#42;Step 3:&#42;&#42; Generate the complete markdown content for a new file, `UI.md`.

---

&#35;&#35; Markdown Output Structure

Use this template for the entire document.

```markdown

&#35; UI Layer Specification

This document specifies the UI layer of the application, breaking it down into pages and reusable components based on the provided sketches. All components will adhere to Ant Design's principles and utilize the data access patterns defined in `docs/guidelines/Zustand-guidelines.md`.

&#35;&#35; 1. High-Level Structure

The application is a single-page application (SPA). It will be composed of a main layout, one primary page, and several reusable components. 

&#35;&#35;&#35; 1.1. `App` Component

The root component that sets up routing and global providers.

-   &#42;&#42;Location&#42;&#42;: `src/App.tsx`
-   &#42;&#42;Purpose&#42;&#42;: To provide global context, including Ant Design's `ConfigProvider` and `App` contexts for message notifications, and to render the main page.
-   &#42;&#42;Composition&#42;&#42;:
  -   Wraps the application with `ConfigProvider` and `App as AntApp` from 'antd' to enable global message notifications as per `simple-ice/antd-messages.mdc`.
  -   Renders `[Page Name]`.

&#35;&#35; 2. Pages

&#35;&#35;&#35; 2.1. `[Page Name]`

-   &#42;&#42;Location:&#42;&#42; `src/pages/PageName.tsx`
-   &#42;&#42;Purpose:&#42;&#42; [Briefly describe the main goal and function of this page]
-   &#42;&#42;Data Access:&#42;&#42;
  [List the specific hooks and functions this component uses to fetch or manage its data]
-   &#42;&#42;Internal State:&#42;&#42;
    [Describe any state managed internally by this page using `useState`]
-   &#42;&#42;Composition:&#42;&#42;
    [Briefly describe the content of this page]
-   &#42;&#42;User Interactions:&#42;&#42;
    [Describe how the user interacts with this page] 
-   &#42;&#42;Logic:&#42;&#42;
  [If applicable, provide additional comments on how this page should work]

&#35;&#35; 3. Components

&#35;&#35;&#35; 3.1. `[Component Name]`

-   &#42;&#42;Location:&#42;&#42; `src/components/ComponentName.tsx`
-   &#42;&#42;Purpose:&#42;&#42; [Explain what this component does and where it's used]
-   &#42;&#42;Props:&#42;&#42;
  [TypeScript interface definition for the component's props. Props should be minimal. Avoid prop drilling by using hooks for data access.]
-   &#42;&#42;Data Access:&#42;&#42;
    [List the specific hooks and functions this component uses to fetch or manage its data]
-   &#42;&#42;Internal State:&#42;&#42;
    [Describe any state managed internally by this component using `useState`]
-   &#42;&#42;Composition:&#42;&#42;
    [Briefly describe the content of this component]
-   &#42;&#42;User Interactions:&#42;&#42;
    [Describe how the user interacts with the component]
-   &#42;&#42;Logic:&#42;&#42;
  [If applicable, provide additional comments on how this component should work]
  
```

--- 

&#35;&#35; Final Instructions

- &#42;&#42;No Assumptions:&#42;&#42; Base every detail on the visual evidence in the sketch, not on common design patterns. 
- &#42;&#42;Double-Check:&#42;&#42; After composing the entire document, read through it to ensure the hierarchy is logical, the descriptions are unambiguous, and the formatting is consistent. The final document should be a self-contained, comprehensive specification. 
- &#42;&#42;Do not add redundant empty lines between items.&#42;&#42; 

Your final output should be the complete, raw markdown content for `UI.md`.
</code></pre>
</div>
</p>
             </div>
         </dd>
          <dt tabindex="0" class="accordion-item" id="accordion-item-3" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 4: DAL Spec to Plan
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-3" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">You are an expert Senior Frontend Developer specializing in React, TypeScript, and Zustand. You are tasked with creating a plan to build a Data Access Layer for an application based on a spec.

&#35;&#35; Workflow

Follow these steps precisely:

&#42;&#42;Step 1:&#42;&#42; Analyze the documentation carefully:

- `DAL.md`: The full technical specification for the Data Access Layer of the application. Follow it carefully and to the letter.

There should be no ambiguity about what we are building.

&#42;&#42;Step 2:&#42;&#42; Check out the guidelines:

- `TS-guidelines.md`: TypeScript Best Practices
- `React-guidelines.md`: React Best Practices
- `Zustand-guidelines.md`: Zustand Best Practices

&#42;&#42;Step 3:&#42;&#42; Create a step-by-step plan to build a Data Access Layer according to the spec. 

Each task should:

- Focus on one concern
- Be reasonably small
- Have a clear start + end
- Contain clearly defined Objectives and Acceptance Criteria

The last step of the plan should include creating a page to test all the capabilities of our Data Access Layer, and making it the start page of this application, so that I can manually check if it works properly. 

I will hand this plan over to an engineering LLM that will be told to complete one task at a time, allowing me to review results in between.

&#35;&#35; Final Instructions
 
- Note that we are not starting from scratch; the basic template has already been created using Vite.
- Do not add redundant empty lines between items.

Your final output should be the complete, raw markdown content for `DAL-plan.md`.
</code></pre>
</div></p>
             </div>
         </dd>
          <dt tabindex="0" class="accordion-item" id="accordion-item-4" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 5: UI Spec to Plan
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-4" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">You are an expert Senior Frontend Developer specializing in React, TypeScript, and the Ant Design library. You are tasked with creating a plan to build a UI layer for an application based on a spec and a sketch.

&#35;&#35; Workflow

Follow these steps precisely:

&#42;&#42;Step 1:&#42;&#42; Analyze the documentation carefully:

- `UI.md`: The full technical specification for the UI layer of the application. Follow it carefully and to the letter.
- `Sketch.png`: Contains important information about the layout and style, complements the UI Layer Specification. The final UI must be as close to this sketch as possible.

There should be no ambiguity about what we are building.

&#42;&#42;Step 2:&#42;&#42; Check out the guidelines:

- `TS-guidelines.md`: TypeScript Best Practices
- `React-guidelines.md`: React Best Practices

&#42;&#42;Step 3:&#42;&#42; Create a step-by-step plan to build a UI layer according to the spec and the sketch. 

Each task must:

- Focus on one concern.
- Be reasonably small.
- Have a clear start + end.
- Result in a verifiable increment of the application. Each increment should be manually testable to allow for functional review and approval before proceeding.
- Contain clearly defined Objectives, Acceptance Criteria, and Manual Testing Plan.

I will hand this plan over to an engineering LLM that will be told to complete one task at a time, allowing me to test in between.

&#35;&#35; Final Instructions

- Note that we are not starting from scratch, the basic template has already been created using Vite, and the Data Access Layer has been built successfully.
- For every task, describe how components should be integrated for verification. You must use the provided hooks to connect to the live Zustand store data—do not use mock data (note that the Data Access Layer has been already built successfully).
- The Manual Testing Plan should read like a user guide. It must only contain actions a user can perform in the browser and must never reference any code files or programming tasks.
- Do not add redundant empty lines between items.

Your final output should be the complete, raw markdown content for `UI-plan.md`.
</code></pre>
</div>
</p>
             </div>
         </dd>         
         <dt tabindex="0" class="accordion-item" id="accordion-item-4" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 6: DAL Plan to Code
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-4" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">You are an expert Senior Frontend Developer specializing in React, TypeScript, and Zustand. You are tasked with building a Data Access Layer for an application based on a spec.

&#35;&#35; Workflow

Follow these steps precisely:

&#42;&#42;Step 1:&#42;&#42; Analyze the documentation carefully:

- @docs/specs/DAL.md: The full technical specification for the Data Access Layer of the application. Follow it carefully and to the letter. 

There should be no ambiguity about what we are building.

&#42;&#42;Step 2:&#42;&#42; Check out the guidelines:

- @docs/guidelines/TS-guidelines.md: TypeScript Best Practices
- @docs/guidelines/React-guidelines.md: React Best Practices
- @docs/guidelines/Zustand-guidelines.md: Zustand Best Practices

&#42;&#42;Step 3:&#42;&#42; Read the plan:

- @docs/plans/DAL-plan.md: The step-by-step plan to build the Data Access Layer of the application.

&#42;&#42;Step 4:&#42;&#42; Build a Data Access Layer for this application according to the spec and following the plan. 

- Complete one task from the plan at a time. 
- After each task, stop, so that I can test it. Don’t move to the next task before I tell you to do so. 
- Do not do anything else. At this point, we are focused on building the Data Access Layer.

&#35;&#35; Final Instructions

- Do not make assumptions based on common patterns; always verify them with the actual data from the spec and the sketch. 
- Do not start the development server, I'll do it by myself.
</code></pre>
</div></p>
             </div>
         </dd>
         <dt tabindex="0" class="accordion-item" id="accordion-item-4" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 7: UI Plan to Code
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-4" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">You are an expert Senior Frontend Developer specializing in React, TypeScript, and the Ant Design library. You are tasked with building a UI layer for an application based on a spec and a sketch.

&#35;&#35; Workflow

Follow these steps precisely:

&#42;&#42;Step 1:&#42;&#42; Analyze the documentation carefully:

- @docs/specs/UI.md: The full technical specification for the UI layer of the application. Follow it carefully and to the letter.
- @docs/intent/Sketch.png: Contains important information about the layout and style, complements the UI Layer Specification. The final UI must be as close to this sketch as possible.
- @docs/specs/DAL.md: The full technical specification for the Data Access Layer of the application. That layer is already ready. Use this spec to understand how to work with it. 

There should be no ambiguity about what we are building.

&#42;&#42;Step 2:&#42;&#42; Check out the guidelines:

- @docs/guidelines/TS-guidelines.md: TypeScript Best Practices
- @docs/guidelines/React-guidelines.md: React Best Practices

&#42;&#42;Step 3:&#42;&#42; Read the plan:

- @docs/plans/UI-plan.md: The step-by-step plan to build the UI layer of the application.

&#42;&#42;Step 4:&#42;&#42; Build a UI layer for this application according to the spec and the sketch, following the step-by-step plan: 

- Complete one task from the plan at a time. 
- Make sure you build the UI according to the sketch; this is very important.
- After each task, stop, so that I can test it. Don’t move to the next task before I tell you to do so. 

&#35;&#35; Final Instructions

- Do not make assumptions based on common patterns; always verify them with the actual data from the spec and the sketch. 
- Follow Ant Design's default styles and components. 
- Do not touch the data access layer: it's ready and it's perfect. 
- Do not start the development server, I'll do it by myself.
</code></pre>
</div></p>
             </div>
         </dd>
         <dt tabindex="0" class="accordion-item" id="accordion-item-4" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 8: TS-guidelines.md
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-4" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">&#35; Guidelines: TypeScript Best Practices

&#35;&#35; Type System & Type Safety

- Use TypeScript for all code and enable strict mode.
- Ensure complete type safety throughout stores, hooks, and component interfaces.
- Prefer interfaces over types for object definitions; use types for unions, intersections, and mapped types.
- Entity interfaces should extend common patterns while maintaining their specific properties.
- Use TypeScript type guards in filtering operations for relationship safety.
- Avoid the 'any' type; prefer 'unknown' when necessary.
- Use generics to create reusable components and functions.
- Utilize TypeScript's features to enforce type safety.
- Use type-only imports (import type { MyType } from './types') when importing types, because verbatimModuleSyntax is enabled.
- Avoid enums; use maps instead.

&#35;&#35; Naming Conventions

- Names should reveal intent and purpose.
- Use PascalCase for component names and types/interfaces.
- Prefix interfaces for React props with 'Props' (e.g., ButtonProps).
- Use camelCase for variables and functions.
- Use UPPER_CASE for constants.
- Use lowercase with dashes for directories, and PascalCase for files with components (e.g., components/auth-wizard/AuthForm.tsx).
- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
- Favor named exports for components.

&#35;&#35; Code Structure & Patterns

- Write concise, technical TypeScript code with accurate examples.
- Use functional and declarative programming patterns; avoid classes.
- Prefer iteration and modularization over code duplication.
- Use the "function" keyword for pure functions.
- Use curly braces for all conditionals for consistency and clarity.
- Structure files appropriately based on their purpose.
- Keep related code together and encapsulate implementation details.

&#35;&#35; Performance & Error Handling

- Use immutable and efficient data structures and algorithms.
- Create custom error types for domain-specific errors.
- Use try-catch blocks with typed catch clauses.
- Handle Promise rejections and async errors properly.
- Log errors appropriately and handle edge cases gracefully.

&#35;&#35; Project Organization

- Place shared types in a types directory.
- Use barrel exports (index.ts) for organizing exports.
- Structure files and directories based on their purpose.

&#35;&#35; Other Rules

- Use comments to explain complex logic or non-obvious decisions.
- Follow the single responsibility principle: each function should do exactly one thing.
- Follow the DRY (Don't Repeat Yourself) principle.
- Do not implement placeholder functions, empty methods, or "just in case" logic. Code should serve the current specification's requirements only.
- Use 2 spaces for indentation (no tabs).
</code></pre>
</div></p>
             </div>
         </dd>
         <dt tabindex="0" class="accordion-item" id="accordion-item-4" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 9: React-guidelines.md
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-4" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">&#35; Guidelines: React Best Practices

&#35;&#35; Component Structure

- Use functional components over class components
- Keep components small and focused
- Extract reusable logic into custom hooks
- Use composition over inheritance
- Implement proper prop types with TypeScript
- Structure React files: exported component, subcomponents, helpers, static content, types
- Use declarative TSX for React components
- Ensure that UI components use custom hooks for data fetching and operations rather than receive data via props, except for simplest components

&#35;&#35; React Patterns

- Utilize useState and useEffect hooks for state and side effects
- Use React.memo for performance optimization when needed
- Utilize React.lazy and Suspense for code-splitting
- Implement error boundaries for robust error handling
- Keep styles close to components

&#35;&#35; React Performance

- Avoid unnecessary re-renders
- Lazy load components and images when possible
- Implement efficient state management
- Optimize rendering strategies
- Optimize network requests
- Employ memoization techniques (e.g., React.memo, useMemo, useCallback)

&#35;&#35; React Project Structure

```
/src
- /components - UI components (every component in a separate file)
- /hooks - public-facing custom hooks (every hook in a separate file)
- /providers - React context providers (every provider in a separate file)
- /pages - page components (every page in a separate file)
- /stores - entity-specific Zustand stores (every store in a separate file)
- /styles - global styles (if needed)
- /types - shared TypeScript types and interfaces
```
</code></pre>
</div></p>
             </div>
         </dd>
         <dt tabindex="0" class="accordion-item" id="accordion-item-4" aria-expanded="false">
              <div class="book__toc__accordion-text">
                <div class="book__toc__chapter-col chapter__title">
                  Appendix 10: Zustand-guidelines.md
                </div>
              </div>
              <div class="accordion-expand-btn-wrapper">
                  <span class="accordion-expand-btn js-accordion-expand-btn">+</span>
              </div>
          </dt>
          <dd style="max-height: none;" class="accordion-desc" id="accordion-desc-4" aria-hidden="true">
              <div class="book__toc__chapter-col chapter__summary">
                <p><div class="break-out">
<pre><code class="language-markdown">&#35; Guidelines: Zustand Best Practices

&#35;&#35; Core Principles

- &#42;&#42;Implement a data layer&#42;&#42; for this React application following this specification carefully and to the letter.
- &#42;&#42;Complete separation of concerns&#42;&#42;: All data operations should be accessible in UI components through simple and clean entity-specific hooks, ensuring state management logic is fully separated from UI logic.
- &#42;&#42;Shared state architecture&#42;&#42;: Different UI components should work with the same shared state, despite using entity-specific hooks separately.

&#35;&#35; Technology Stack

- &#42;&#42;State management&#42;&#42;: Use Zustand for state management with automatic localStorage persistence via the `persist` middleware.

&#35;&#35; Store Architecture

- &#42;&#42;Base entity:&#42;&#42; Implement a BaseEntity interface with common properties that all entities extend:
```typescript 
export interface BaseEntity { 
  id: string; 
  createdAt: string; // ISO 8601 format 
  updatedAt: string; // ISO 8601 format 
}
```
- &#42;&#42;Entity-specific stores&#42;&#42;: Create separate Zustand stores for each entity type.
- &#42;&#42;Dictionary-based storage&#42;&#42;: Use dictionary/map structures (`Record<string, Entity>`) rather than arrays for O(1) access by ID.
- &#42;&#42;Handle relationships&#42;&#42;: Implement cross-entity relationships (like cascade deletes) within the stores where appropriate.

&#35;&#35; Hook Layer

The hook layer is the exclusive interface between UI components and the Zustand stores. It is designed to be simple, predictable, and follow a consistent pattern across all entities.

&#35;&#35;&#35; Core Principles

1.  &#42;&#42;One Hook Per Entity&#42;&#42;: There will be a single, comprehensive custom hook for each entity (e.g., `useBlogPosts`, `useCategories`). This hook is the sole entry point for all data and operations related to that entity. Separate hooks for single-item access will not be created.
2.  &#42;&#42;Return reactive data, not getter functions&#42;&#42;: To prevent stale data, hooks must return the state itself, not a function that retrieves state. Parameterize hooks to accept filters and return the derived data directly. A component calling a getter function will not update when the underlying data changes.
3.  &#42;&#42;Expose Dictionaries for O(1) Access&#42;&#42;: To provide simple and direct access to data, every hook will return a dictionary (`Record<string, Entity>`) of the relevant items.

&#35;&#35;&#35; The Standard Hook Pattern

Every entity hook will follow this implementation pattern:

1.  &#42;&#42;Subscribe&#42;&#42; to the entire dictionary of entities from the corresponding Zustand store. This ensures the hook is reactive to any change in the data.
2.  &#42;&#42;Filter&#42;&#42; the data based on the parameters passed into the hook. This logic will be memoized with `useMemo` for efficiency. If no parameters are provided, the hook will operate on the entire dataset.
3.  &#42;&#42;Return a Consistent Shape&#42;&#42;: The hook will always return an object containing:
    &#42;   A &#42;&#42;filtered and sorted array&#42;&#42; (e.g., `blogPosts`) for rendering lists.
    &#42;   A &#42;&#42;filtered dictionary&#42;&#42; (e.g., `blogPostsDict`) for convenient `O(1)` lookup within the component.
    &#42;   All necessary &#42;&#42;action functions&#42;&#42; (`add`, `update`, `remove`) and &#42;&#42;relationship operations&#42;&#42;.
    &#42;   All necessary &#42;&#42;helper functions&#42;&#42; and &#42;&#42;derived data objects&#42;&#42;. Helper functions are suitable for pure, stateless logic (e.g., calculators). Derived data objects are memoized values that provide aggregated or summarized information from the state (e.g., an object containing status counts). They must be derived directly from the reactive state to ensure they update automatically when the underlying data changes.

&#35;&#35; API Design Standards

- &#42;&#42;Object Parameters&#42;&#42;: Use object parameters instead of multiple direct parameters for better extensibility:
```typescript

// ✅ Preferred

add({ title, categoryIds })

// ❌ Avoid

add(title, categoryIds)

```
- &#42;&#42;Internal Methods&#42;&#42;: Use underscore-prefixed methods for cross-store operations to maintain clean separation.

&#35;&#35; State Validation Standards

- &#42;&#42;Existence checks&#42;&#42;: All `update` and `remove` operations should validate entity existence before proceeding.
- &#42;&#42;Relationship validation&#42;&#42;: Verify both entities exist before establishing relationships between them.

&#35;&#35; Error Handling Patterns

- &#42;&#42;Operation failures&#42;&#42;: Define behavior when operations fail (e.g., updating non-existent entities).
- &#42;&#42;Graceful degradation&#42;&#42;: How to handle missing related entities in helper functions.

&#35;&#35; Other Standards

- &#42;&#42;Secure ID generation&#42;&#42;: Use `crypto.randomUUID()` for entity ID generation instead of custom implementations for better uniqueness guarantees and security.
- &#42;&#42;Return type consistency&#42;&#42;: `add` operations return generated IDs for component workflows requiring immediate entity access, while `update` and `remove` operations return `void` to maintain clean modification APIs.
</code></pre>
</div></p>
             </div>
         </dd>    
    <span></span></dl>
</div>
                

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item><item><author>Lyndon Cerejo</author><title>From Prompt To Partner: Designing Your Custom AI Assistant</title><link>https://www.smashingmagazine.com/2025/09/from-prompt-to-partner-designing-custom-ai-assistant/</link><pubDate>Fri, 26 Sep 2025 10:00:00 +0000</pubDate><guid>https://www.smashingmagazine.com/2025/09/from-prompt-to-partner-designing-custom-ai-assistant/</guid><description>What if your best AI prompts didn’t disappear into your unorganized chat history, but came back tomorrow as a reliable assistant? In this article, you’ll learn how to turn one-off “aha” prompts into reusable assistants that are tailored to your audience, grounded in your knowledge, and consistent every time, saving you (and your team) from typing the same 448-word prompt ever again. No coding, just designing, and by the end, you’ll have a custom AI assistant that can augment your team.</description><content:encoded><![CDATA[
          <html>
            <head>
              <meta charset="utf-8">
              <link rel="canonical" href="https://www.smashingmagazine.com/2025/09/from-prompt-to-partner-designing-custom-ai-assistant/" />
              <title>From Prompt To Partner: Designing Your Custom AI Assistant</title>
            </head>
            <body>
              <article>
                <header>
                  <h1>From Prompt To Partner: Designing Your Custom AI Assistant</h1>
                  
                    
                    <address>Lyndon Cerejo</address>
                  
                  <time datetime="2025-09-26T10:00:00&#43;00:00" class="op-published">2025-09-26T10:00:00+00:00</time>
                  <time datetime="2025-09-26T10:00:00&#43;00:00" class="op-modified">2025-12-25T09:32:05+00:00</time>
                </header>
                
                

<p>In “<a href="https://www.smashingmagazine.com/2025/08/week-in-life-ai-augmented-designer/">A Week In The Life Of An AI-Augmented Designer</a>”, Kate stumbled her way through an AI-augmented sprint (coffee was chugged, mistakes were made). In “<a href="https://www.smashingmagazine.com/2025/08/prompting-design-act-brief-guide-iterate-ai/">Prompting Is A Design Act</a>”, we introduced WIRE+FRAME, a framework to structure prompts like designers structure creative briefs. Now we’ll take the next step: packaging those structured prompts into AI assistants you can design, reuse, and share.</p>

<p>AI assistants go by different names: CustomGPTs (ChatGPT), Agents (Copilot), and Gems (Gemini). But they all serve the same function &mdash; allowing you to customize the default AI model for your unique needs. If we carry over our smart intern analogy, think of these as interns trained to assist you with specific tasks, eliminating the need for repeated instructions or information, and who can support not just you, but your entire team.</p>

<h2 id="why-build-your-own-assistant">Why Build Your Own Assistant?</h2>

<p>If you’ve ever copied and pasted the same mega-prompt for the n<sup>th</sup> time, you’ve experienced the pain. An AI assistant turns a one-off “great prompt” into a dependable teammate. And if you’ve used any of the publicly available AI Assistants, you’ve realized quickly that they’re usually generic and not tailored for your use.</p>

<p>Public AI assistants are great for inspiration, but nothing beats an assistant that solves a repeated problem for you and your team, in <strong>your voice</strong>, with <strong>your context and constraints</strong> baked in. Instead of reinventing the wheel by writing new prompts each time, or repeatedly copy-pasting your structured prompts every time, or spending cycles trying to make a public AI Assistant work the way you need it to, your own AI Assistant allows you and others to easily get better, repeatable, consistent results faster.</p>

<h3 id="benefits-of-reusing-prompts-even-your-own">Benefits Of Reusing Prompts, Even Your Own</h3>

<p>Some of the benefits of building your own AI Assistant over writing or reusing your prompts include:</p>

<ul>
<li><strong>Focused on a real repeating problem</strong><br />
A good AI Assistant isn’t a general-purpose “do everything” bot that you need to keep tweaking. It focuses on a single, recurring problem that takes a long time to complete manually and often results in varying quality depending on who’s doing it (e.g., analyzing customer feedback).</li>
<li><strong>Customized for your context</strong><br />
Most large language models (LLMs, such as ChatGPT) are designed to be everything to everyone. An AI Assistant changes that by allowing you to customize it to automatically work like you want it to, instead of a generic AI.</li>
<li><strong>Consistency at scale</strong><br />
You can use the <a href="https://www.smashingmagazine.com/2025/08/prompting-design-act-brief-guide-iterate-ai/#anatomy-structure-it-like-a-designer">WIRE+FRAME prompt framework</a> to create structured, reusable prompts. An AI Assistant is the next logical step: instead of copy-pasting that fine-tuned prompt and sharing contextual information and examples each time, you can bake it into the assistant itself, allowing you and others achieve the same consistent results every time.</li>
<li><strong>Codifying expertise</strong><br />
Every time you turn a great prompt into an AI Assistant, you’re essentially bottling your expertise. Your assistant becomes a living design guide that outlasts projects (and even job changes).</li>
<li><strong>Faster ramp-up for teammates</strong><br />
Instead of new designers starting from a blank slate, they can use pre-tuned assistants. Think of it as knowledge transfer without the long onboarding lecture.

<br /></li>
</ul>

<div data-audience="non-subscriber" data-remove="true" class="feature-panel-container">

<aside class="feature-panel" style="">
<div class="feature-panel-left-col">

<div class="feature-panel-description"><p>Meet <strong><a data-instant href="https://www.smashingconf.com/online-workshops/">Smashing Workshops</a></strong> on <strong>front-end, design &amp; UX</strong>, with practical takeaways, live sessions, <strong>video recordings</strong> and a friendly Q&amp;A. With Brad Frost, Stéph Walter and <a href="https://smashingconf.com/online-workshops/workshops">so many others</a>.</p>
<a data-instant href="smashing-workshops" class="btn btn--green btn--large" style="">Jump to the workshops&nbsp;↬</a></div>
</div>
<div class="feature-panel-right-col"><a data-instant href="smashing-workshops" class="feature-panel-image-link">
<div class="feature-panel-image">
<img
    loading="lazy"
    decoding="async"
    class="feature-panel-image-img"
    src="/images/smashing-cat/cat-scubadiving-panel.svg"
    alt="Feature Panel"
    width="257"
    height="355"
/>

</div>
</a>
</div>
</aside>
</div>

<h3 id="reasons-for-your-own-ai-assistant-instead-of-public-ai-assistants">Reasons For Your Own AI Assistant Instead Of Public AI Assistants</h3>

<p>Public AI assistants are like stock templates. While they serve a specific purpose compared to the generic AI platform, and are useful starting points, if you want something tailored to your needs and team, you should really build your own.</p>

<p>A few reasons for building your AI Assistant instead of using a public assistant someone else created include:</p>

<ul>
<li><strong>Fit</strong>: Public assistants are built for the masses. Your work has quirks, tone, and processes they’ll never quite match.</li>
<li><strong>Trust &amp; Security</strong>: You don’t control what instructions or hidden guardrails someone else baked in. With your own assistant, you know exactly what it will (and won’t) do.</li>
<li><strong>Evolution</strong>: An AI Assistant you design and build can grow with your team. You can update files, tweak prompts, and maintain a changelog &mdash; things a public bot won’t do for you.</li>
</ul>

<p>Your own AI Assistants allow you to take your successful ways of interacting with AI and make them repeatable and shareable. And while they are tailored to your and your team’s way of working, remember that they are still based on generic AI models, so the usual AI disclaimers apply:</p>

<p><em>Don’t share anything you wouldn’t want screenshotted in the next company all-hands. Keep it safe, private, and user-respecting. A shared AI Assistant can potentially reveal its inner workings or data.</em></p>

<p><strong><em>Note</em></strong>: <em>We will be building an AI assistant using ChatGPT, aka a CustomGPT, but you can try the same process with any decent LLM sidekick. As of publication, a paid account is required to create CustomGPTs, but once created, they can be shared and used by anyone, regardless of whether they have a paid or free account. Similar limitations apply to the other platforms. Just remember that outputs can vary depending on the LLM model used, the model’s training, mood, and flair for creative hallucinations.</em></p>

<h3 id="when-not-to-build-an-ai-assistant-yet">When Not to Build An AI Assistant (Yet)</h3>

<p>An AI Assistant is great when the <em>same</em> audience has the <em>same</em> problem <em>often</em>. When the fit isn’t there, the risk is high; you should skip building an AI Assistant for now, as explained below:</p>

<ul>
<li><strong>One-off or rare tasks</strong><br />
If it won’t be reused at least monthly, I’d recommend keeping it as a saved WIRE+FRAME prompt. For example, something for a one-time audit or creating placeholder content for a specific screen.</li>
<li><strong>Sensitive or regulated data</strong><br />
If you need to build in personally identifiable information (PII), health, finance, legal, or trade secrets, err on the side of not building an AI Assistant. Even if the AI platform promises not to use your data, I’d strongly suggest using redaction or an approved enterprise tool with necessary safeguards in place (company-approved enterprise versions of Microsoft Copilot, for instance).</li>
<li><strong>Heavy orchestration or logic</strong><br />
Multi-step workflows, API calls, database writes, and approvals go beyond the scope of an AI Assistant into Agentic territory (as of now). I’d recommend not trying to build an AI Assistant for these cases.</li>
<li><strong>Real-time information</strong><br />
AI Assistants may not be able to access real-time data like prices, live metrics, or breaking news. If you need these, you can upload near-real-time data (as we do below) or connect with data sources that you or your company controls, rather than relying on the open web.</li>
<li><strong>High-stakes outputs</strong><br />
For cases related to compliance, legal, medical, or any other area requiring auditability, consider implementing process guardrails and training to keep humans in the loop for proper review and accountability.</li>
<li><strong>No measurable win</strong><br />
If you can’t name a success metric (such as time saved, first-draft quality, or fewer re-dos), I’d recommend keeping it as a saved WIRE+FRAME prompt.</li>
</ul>

<p>Just because these are signs that you should not build your AI Assistant now, doesn’t mean you shouldn’t ever. Revisit this decision when you notice that you’re starting to repeatedly use the same prompt weekly, multiple teammates ask for it, or manual time copy-pasting and refining start exceeding ~15 minutes. Those are some signs that an AI Assistant will pay back quickly.</p>

<p>In a nutshell, build an AI Assistant when you can name the problem, the audience, frequency, and the win. The rest of this article shows how to turn your successful WIRE+FRAME prompt into a CustomGPT that you and your team can actually use. No advanced knowledge, coding skills, or hacks needed.</p>

<h2 id="as-always-start-with-the-user">As Always, Start with the User</h2>

<p>This should go without saying to UX professionals, but it’s worth a reminder: if you’re building an AI assistant for anyone besides yourself, start with the user and their needs before you build anything.</p>

<ul>
<li>Who will use this assistant?</li>
<li>What’s the specific pain or task they struggle with today?</li>
<li>What language, tone, and examples will feel natural to them?</li>
</ul>

<p>Building without doing this first is a sure way to end up with clever assistants nobody actually wants to use. Think of it like any other product: before you build features, you understand your audience. The same rule applies here, even more so, because AI assistants are only as helpful as they are useful and usable.</p>

<h2 id="from-prompt-to-assistant">From Prompt To Assistant</h2>

<p>You’ve already done the heavy lifting with WIRE+FRAME. Now you’re just turning that refined and reliable prompt into a CustomGPT you can reuse and share. You can use MATCH as a checklist to go from a great prompt to a useful AI assistant.</p>

<ul>
<li><strong>M: Map your prompt</strong><br />
Port your successful WIRE+FRAME prompt into the AI assistant.</li>
<li><strong>A: Add knowledge and training</strong><br />
Ground the assistant in <em>your</em> world. Upload knowledge files, examples, or guides that make it uniquely yours.</li>
<li><strong>T: Tailor for audience</strong><br />
Make it feel natural to the people who will use it. Give it the right capabilities, but also adjust its settings, tone, examples, and conversation starters so they land with your audience.</li>
<li><strong>C: Check, test, and refine</strong><br />
Test the preview with different inputs and refine until you get the results you expect.</li>
<li><strong>H: Hand off and maintain</strong><br />
Set sharing options and permissions, share the link, and maintain it.</li>
</ul>

<p>A few weeks ago, we invited readers to share their ideas for AI assistants they wished they had. The top contenders were:</p>

<ul>
<li><strong>Prototype Prodigy</strong>: Transform rough ideas into prototypes and export them into Figma to refine.</li>
<li><strong>Critique Coach</strong>: Review wireframes or mockups and point out accessibility and usability gaps.</li>
</ul>

<p>But the favorite was an AI assistant to turn tons of customer feedback into actionable insights. Readers replied with variations of: <em>“An assistant that can quickly sort through piles of survey responses, app reviews, or open-ended comments and turn them into themes we can act on.”</em></p>

<p>And that’s the one we will build in this article &mdash; say hello to <strong>Insight Interpreter.</strong></p>

<div class="partners__lead-place"></div>

<h2 id="walkthrough-insight-interpreter">Walkthrough: Insight Interpreter</h2>

<p>Having lots of customer feedback is a nice problem to have. Companies actively seek out customer feedback through surveys and studies (solicited), but also receive feedback that may not have been asked for through social media or public reviews (unsolicited). This is a goldmine of information, but it can be messy and overwhelming trying to make sense of it all, and it’s nobody’s idea of fun. Here’s where an AI assistant like the Insight Interpreter can help. We’ll turn the example prompt created using the WIRE+FRAME framework in <a href="https://www.smashingmagazine.com/2025/08/prompting-design-act-brief-guide-iterate-ai/">Prompting Is A Design Act</a> into a CustomGPT.</p>

<p>When you start building a CustomGPT by visiting <a href="https://chat.openai.com/gpts/editor?utm_source=chatgpt.com">https://chat.openai.com/gpts/editor</a>, you’ll see two paths:</p>

<ul>
<li><strong>Conversational interface</strong><br />
Vibe-chat your way &mdash; it’s easy and quick, but similar to unstructured prompts, your inputs get baked in a little messily, so you may end up with vague or inconsistent instructions.</li>
<li><strong>Configure interface</strong><br />
The structured form where you type instructions, upload files, and toggle capabilities. Less instant gratification, less winging it, but more control. This is the option you’ll want for assistants you plan to share or depend on regularly.</li>
</ul>

<p>The good news is that MATCH works for both. In conversational mode, you can use it as a mental checklist, and we’ll walk through using it in configure mode as a more formal checklist in this article.</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="451"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png"
			
			sizes="100vw"
			alt="CustomGPT Configure Interface"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      CustomGPT Configure Interface. (<a href='https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/1-customgpt-configure-interface.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h3 id="m-map-your-prompt">M: Map Your Prompt</h3>

<p>Paste your full WIRE+FRAME prompt into the <em>Instructions</em> section exactly as written. As a refresher, I’ve included the mapping and snippets of the detailed prompt from before:</p>

<ul>
<li><strong>W</strong>ho &amp; What: The AI persona and the core deliverable (<em>“…senior UX researcher and customer insights analyst… specialize in synthesizing qualitative data from diverse sources…”</em>).</li>
<li><strong>I</strong>nput Context: Background or data scope to frame the task (<em>“…analyzing customer feedback uploaded from sources such as…”</em>).</li>
<li><strong>R</strong>ules &amp; Constraints: Boundaries (<em>“…do not fabricate pain points, representative quotes, journey stages, or patterns…”</em>).</li>
<li><strong>E</strong>xpected Output: Format and fields of the deliverable (<em>“…a structured list of themes. For each theme, include…”</em>).</li>
<li><strong>F</strong>low: Explicit, ordered sub-tasks (<em>“Recommended flow of tasks: Step 1…”</em>).</li>
<li><strong>R</strong>eference Voice: Tone, mood, or reference (<em>“…concise, pattern-driven, and objective…”</em>).</li>
<li><strong>A</strong>sk for Clarification: Ask questions if unclear (<em>“…if data is missing or unclear, ask before continuing…”</em>).</li>
<li><strong>M</strong>emory: Memory to recall earlier definitions (<em>“Unless explicitly instructed otherwise, keep using this process…”</em>).</li>
<li><strong>E</strong>valuate &amp; Iterate: Have the AI self-critique outputs (<em>“…critically evaluate…suggest improvements…”</em>).</li>
</ul>

<p>If you’re building Copilot Agents or Gemini Gems instead of CustomGPTs, you still paste your WIRE+FRAME prompt into their respective <em>Instructions</em> sections.</p>

<h3 id="a-add-knowledge-and-training">A: Add Knowledge And Training</h3>

<p>In the knowledge section, upload up to 20 files, clearly labeled, that will help the CustomGPT respond effectively. Keep files small and versioned: <em>reviews_Q2_2025.csv</em> beats <em>latestfile_final2.csv</em>. For this prompt for analyzing customer feedback, generating themes organized by customer journey, rating them by severity and effort, files could include:</p>

<ul>
<li>Taxonomy of themes;</li>
<li>Instructions on parsing uploaded data;</li>
<li>Examples of real UX research reports using this structure;</li>
<li>Scoring guidelines for severity and effort, e.g., what makes something a 3 vs. a 5 in severity;</li>
<li>Customer journey map stages;</li>
<li>Customer feedback file templates (not actual data).</li>
</ul>

<p>An example of a file to help it parse uploaded data is shown below:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="447"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png"
			
			sizes="100vw"
			alt="GPT file parsing instructions"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      (<a href='https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/2-gpt-file-parsing-instructions.png'>Large preview</a>)
    </figcaption>
  
</figure>

<h3 id="t-tailor-for-audience">T: Tailor For Audience</h3>

<ul>
<li><strong>Audience tailoring</strong><br />
If you are building this for others, your prompt should have addressed tone in the “Reference Voice” section. If you didn’t, do it now, so the CustomGPT can be tailored to the tone and expertise level of users who will use it. In addition, use the <em>Conversation starters</em> section to add a few examples or common prompts for users to start using the CustomGPT, again, worded for your users. For instance, we could use “Analyze feedback from the attached file” for our Insights Interpreter to make it more self-explanatory for anyone, instead of “Analyze data,” which may be good enough if you were using it alone. For my Designerly Curiosity GPT, assuming that users may not know what it could do, I use “What are the types of curiosity?” and “Give me a micro-practice to spark curiosity”.</li>
<li><strong>Functional tailoring</strong><br />
Fill in the CustomGPT name, icon, description, and capabilities.

<ul>
<li><em>Name</em>: Pick one that will make it clear what the CustomGPT does. Let’s use “Insights Interpreter &mdash; Customer Feedback Analyzer”. If needed, you can also add a version number. This name will show up in the sidebar when people use it or pin it, so make the first part memorable and easily identifiable.</li>
<li><em>Icon</em>: Upload an image or generate one. Keep it simple so it can be easily recognized in a smaller dimension when people pin it in their sidebar.</li>
<li><em>Description</em>: A brief, yet clear description of what the CustomGPT can do. If you plan to list it in the GPT store, this will help people decide if they should pick yours over something similar.</li>
<li><em>Recommended Model</em>: If your CustomGPT needs the capabilities of a particular model (e.g., needs GPT-5 thinking for detailed analysis), select it. In most cases, you can safely leave it up to the user or select the most common model.</li>
<li><em>Capabilities</em>: Turn off anything you won’t need. We’ll turn off “Web Search” to allow the CustomGPT to focus only on uploaded data, without expanding the search online, and we will turn on “Code Interpreter &amp; Data Analysis” to allow it to understand and process uploaded files. “Canvas” allows users to work on a shared canvas with the GPT to edit writing tasks; “Image generation” - if the CustomGPT needs to create images.</li>
<li><em>Actions</em>: Making <a href="https://platform.openai.com/docs/actions/introduction">third-party APIs</a> available to the CustomGPT, advanced functionality we don’t need.</li>
<li><em>Additional Settings</em>: Sneakily hidden and opted in by default, I opt out of training OpenAI’s models.</li>
</ul></li>
</ul>

<h3 id="c-check-test-refine">C: Check, Test &amp; Refine</h3>

<p>Do one last visual check to make sure you’ve filled in all applicable fields and the basics are in place: is the concept sharp and clear (not a do-everything bot)? Are the roles, goals, and tone clear? Do we have the right assets (docs, guides) to support it? Is the flow simple enough that others can get started easily? Once those boxes are checked, move into testing.</p>

<p>Use the <em>Preview</em> panel to verify that your CustomGPT performs as well, or better, than your original WIRE+FRAME prompt, and that it works for your intended audience. Try a few representative inputs and compare the results to what you expected. If something worked before but doesn’t now, check whether new instructions or knowledge files are overriding it.</p>

<p>When things don’t look right, here are quick debugging fixes:</p>

<ul>
<li><strong>Generic answers?</strong><br />
Tighten <em>Input Context</em> or update the knowledge files.</li>
<li><strong>Hallucinations?</strong><br />
Revisit your <em>Rules</em> section. Turn off web browsing if you don’t need external data.</li>
<li><strong>Wrong tone?</strong><br />
Strengthen <em>Reference Voice</em> or swap in clearer examples.</li>
<li><strong>Inconsistent?</strong><br />
Test across models in preview and set the most reliable one as “Recommended.”</li>
</ul>

<h3 id="h-hand-off-and-maintain">H: Hand Off And Maintain</h3>

<p>When your CustomGPT is ready, you can publish it via the “Create” option. Select the appropriate access option:</p>

<ul>
<li><strong>Only me</strong>: Private use. Perfect if you’re still experimenting or keeping it personal.</li>
<li><strong>Anyone with the link</strong>: Exactly what it means. Shareable but not searchable. Great for pilots with a team or small group. Just remember that links can be reshared, so treat them as semi-public.</li>
<li><strong>GPT Store</strong>: Fully public. Your assistant is listed and findable by anyone browsing the store. <em>(This is the option we’ll use.)</em></li>
<li><strong>Business workspace</strong> (if you’re on GPT Business): Share with others within your business account only &mdash; the easiest way to keep it in-house and controlled.</li>
</ul>

<p>But hand off doesn’t end with hitting publish, you should maintain it to keep it relevant and useful:</p>

<ul>
<li><strong>Collect feedback</strong>: Ask teammates what worked, what didn’t, and what they had to fix manually.</li>
<li><strong>Iterate</strong>: Apply changes directly or duplicate the GPT if you want multiple versions in play. You can find all your CustomGPTs at: <a href="https://chatgpt.com/gpts/mine">https://chatgpt.com/gpts/mine</a></li>
<li><strong>Track changes</strong>: Keep a simple changelog (date, version, updates) for traceability.</li>
<li><strong>Refresh knowledge</strong>: Update knowledge files and examples on a regular cadence so answers don’t go stale.</li>
</ul>

<p>And that’s it! <a href="https://go.cerejo.com/insights-interpreter">Our Insights Interpreter is now live!</a></p>

<p>Since we used the WIRE+FRAME prompt from the previous article to create the Insights Interpreter CustomGPT, I compared the outputs:</p>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="325"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png"
			
			sizes="100vw"
			alt="Results of the structured WIRE&#43;FRAME prompt from the previous article"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Results of the structured WIRE+FRAME prompt from the previous article. (<a href='https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/3-results-structured-wire-frame-prompt.png'>Large preview</a>)
    </figcaption>
  
</figure>














<figure class="
  
    break-out article__image
  
  
  ">
  
    <a href="https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png">
    
    <img
      loading="lazy"
      decoding="async"
      fetchpriority="low"
			width="800"
			height="276"
			
			srcset="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png 400w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_800/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png 800w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1200/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png 1200w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_1600/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png 1600w,
			        https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_2000/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png 2000w"
			src="https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_80/w_400/https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png"
			
			sizes="100vw"
			alt="Results of the Insights Interpreter CustomGPT based on the same prompt"
		/>
    
    </a>
  

  
    <figcaption class="op-vertical-bottom">
      Results of the Insights Interpreter CustomGPT based on the same prompt. (<a href='https://files.smashing.media/articles/from-prompt-to-partner-designing-custom-ai-assistant/4-results-insights-interpreter-customgpt.png'>Large preview</a>)
    </figcaption>
  
</figure>

<p>The results are similar, with slight differences, and that’s expected. If you compare the results carefully, the themes, issues, journey stages, frequency, severity, and estimated effort match with some differences in wording of the theme, issue summary, and problem statement. The opportunities and quotes have more visible differences. Most of it is because of the CustomGPT knowledge and training files, including instructions, examples, and guardrails, now live as always-on guidance.</p>

<p>Keep in mind that in reality, Generative AI is by nature generative, so outputs will vary. Even with the same data, you won’t get identical wording every time. In addition, underlying models and their capabilities rapidly change. If you want to keep things as consistent as possible, recommend a model (though people can change it), track versions of your data, and compare for structure, priorities, and evidence rather than exact wording.</p>

<p>While I’d love for you to use Insights Interpreter, I strongly recommend taking 15 minutes to follow the steps above and create your own. That is exactly what you or your team needs &mdash; including the tone, context, output formats, and get the real AI Assistant you need!</p>

<div class="partners__lead-place"></div>

<h2 id="inspiration-for-other-ai-assistants">Inspiration For Other AI Assistants</h2>

<p>We just built the Insight Interpreter and mentioned two contenders: Critique Coach and Prototype Prodigy. Here are a few other realistic uses that can spark ideas for your own AI Assistant:</p>

<ul>
<li><strong>Workshop Wizard</strong>: Generates workshop agendas, produces icebreaker questions, and follows up survey drafts.</li>
<li><strong>Research Roundup Buddy</strong>: Summarizes raw transcripts into key themes, then creates highlight reels (quotes + visuals) for team share-outs.</li>
<li><strong>Persona Refresher</strong>: Updates stale personas with the latest customer feedback, then rewrites them in different tones (boardroom formal vs. design-team casual).</li>
<li><strong>Content Checker</strong>: Proofs copy for tone, accessibility, and reading level before it ever hits your site.</li>
<li><strong>Trend Tamer</strong>: Scans competitor reviews and identifies emerging patterns you can act on before they reach your roadmap.</li>
<li><strong>Microcopy Provocateur</strong>: Tests alternate copy options by injecting different tones (sassy, calm, ironic, nurturing) and role-playing how users might react, especially useful for error states or Call to Actions.</li>
<li><strong>Ethical UX Debater</strong>: Challenges your design decisions and deceptive designs by simulating the voice of an ethics board or concerned user.</li>
</ul>

<p>The best AI Assistants come from carefully inspecting your workflow and looking for areas where AI can augment your work regularly and repetitively. Then follow the steps above to build a team of customized AI assistants.</p>

<h2 id="ask-me-anything-about-assistants">Ask Me Anything About Assistants</h2>

<ul>
<li><strong>What are some limitations of a CustomGPT?</strong><br />
Right now, the best parallels for AI are a very smart intern with access to a lot of information. CustomGPTs are still running on LLM models that are basically trained on a lot of information and programmed to predictively generate responses based on that data, including possible bias, misinformation, or incomplete information. Keeping that in mind, you can make that intern provide better and more relevant results by using your uploads as onboarding docs, your guardrails as a job description, and your updates as retraining.</li>
<li><strong>Can I copy someone else’s public CustomGPT and tweak it?</strong><br />
Not directly, but if you get inspired by another CustomGPT, you can look at how it’s framed and rebuild your own using WIRE+FRAME &amp; MATCH. That way, you make it your own and have full control of the instructions, files, and updates. But you can do that with Google’s equivalent &mdash; Gemini Gems. Shared Gems behave similarly to shared Google Docs, so once shared, any Gem instructions and files that you have uploaded can be viewed by any user with access to the Gem. Any user with edit access to the Gem can also update and delete the Gem.</li>
<li><strong>How private are my uploaded files?</strong><br />
The files you upload are stored and used to answer prompts to your CustomGPT. If your CustomGPT is not private or you didn’t disable the hidden setting to allow CustomGPT conversations to improve the model, that data could be referenced. Don’t upload sensitive, confidential, or personal data you wouldn’t want circulating. Enterprise accounts do have some protections, so check with your company.</li>
<li><strong>How many files can I upload, and does size matter?</strong><br />
Limits vary by platform, but smaller, specific files usually perform better than giant docs. Think “chapter” instead of “entire book.” At the time of publishing, CustomGPTs allow up to 20 files, Copilot Agents up to 200 (if you need anywhere near that many, chances are your agent is not focused enough), and Gemini Gems up to 10.</li>
<li><strong>What’s the difference between a CustomGPT and a Project?</strong><br />
A CustomGPT is a focused assistant, like an intern trained to do one role well (like “Insight Interpreter”). A Project is more like a workspace where you can group multiple prompts, files, and conversations together for a broader effort. CustomGPTs are specialists. Projects are containers. If you want something reusable, shareable, and role-specific, go to CustomGPT. If you want to organize broader work with multiple tools and outputs, and shared knowledge, Projects are the better fit.</li>
</ul>

<h2 id="from-reading-to-building">From Reading To Building</h2>

<p>In this AI x Design series, we’ve gone from messy prompting (“<a href="https://www.smashingmagazine.com/2025/08/week-in-life-ai-augmented-designer/">A Week In The Life Of An AI-Augmented Designer</a>”) to a structured prompt framework, WIRE+FRAME (“<a href="https://www.smashingmagazine.com/2025/08/prompting-design-act-brief-guide-iterate-ai/">Prompting Is A Design Act</a>”). And now, in this article, your very own reusable AI sidekick.</p>

<p>CustomGPTs don’t replace designers but augment them. The real magic isn’t in the tool itself, but in <em>how</em> you design and manage it. You can use public CustomGPTs for inspiration, but the ones that truly fit your workflow are the ones you design yourself. They <strong>extend your craft</strong>, <strong>codify your expertise</strong>, and give your team leverage that generic AI models can’t.</p>

<p>Build one this week. Even better, today. Train it, share it, stress-test it, and refine it into an AI assistant that can augment your team.</p>

<div class="signature">
  <img src="https://www.smashingmagazine.com/images/logo/logo--red.png" alt="Smashing Editorial" width="35" height="46" loading="lazy" decoding="async" />
  <span>(yk)</span>
</div>


              </article>
            </body>
          </html>
        ]]></content:encoded></item></channel></rss>