<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[UI Engineering Excellence]]></title><description><![CDATA[for those with one goal: to be the best engineer and build the best apps]]></description><link>https://blog.robhameetman.com</link><image><url>https://substackcdn.com/image/fetch/$s_!deTI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png</url><title>UI Engineering Excellence</title><link>https://blog.robhameetman.com</link></image><generator>Substack</generator><lastBuildDate>Sun, 17 May 2026 00:00:06 GMT</lastBuildDate><atom:link href="https://blog.robhameetman.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Robert Henry Hameetman]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[robhameetman@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[robhameetman@substack.com]]></itunes:email><itunes:name><![CDATA[Rob Hameetman]]></itunes:name></itunes:owner><itunes:author><![CDATA[Rob Hameetman]]></itunes:author><googleplay:owner><![CDATA[robhameetman@substack.com]]></googleplay:owner><googleplay:email><![CDATA[robhameetman@substack.com]]></googleplay:email><googleplay:author><![CDATA[Rob Hameetman]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Architecting Design Systems for Human Perception]]></title><description><![CDATA[From art theory to code: cognitive framing that boosts discoverability, consistency, and delivery velocity.]]></description><link>https://blog.robhameetman.com/p/design-systems-for-human-perception</link><guid isPermaLink="false">https://blog.robhameetman.com/p/design-systems-for-human-perception</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Mon, 25 Aug 2025 22:53:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/dec40623-1506-4735-bb21-e324828114e3_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mIVN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mIVN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!mIVN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!mIVN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!mIVN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mIVN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2345606,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mIVN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!mIVN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!mIVN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!mIVN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff7b56674-e107-4a7b-b36b-525890194457_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Back in 1993, Donald Norman coined &#8216;user experience&#8217; to remind us that emotions, behavior, and satisfaction should steer product decisions.</p><p>Design tokens- simple name&#8209;value pairs for colors, type, spacing, and more- let us compose those experiences in code.</p><p>A clear token hierarchy allows for flexible theming and gives design and engineering a ubiquitous visual language.</p><p>One common approach bundles lower&#8209;level <em>options</em> into higher&#8209;level <em>decisions</em>. Many teams split tokens into three or four layers to keep things organized.</p><blockquote><p><em>&#8220;How to convey the idea that, when designing a product, we&#8217;re not just assembling blocks&#8230; But we also want our user to feel something.&#8221;<br>&#8212; </em>Audrey Hacq, <a href="https://uxdesign.cc/atomic-design-creativity-28ef74d71bc6">Atomic Design &amp; creativity</a></p></blockquote><p><strong>The Problem: </strong>Design systems treat tokens as subatomic design particles, ignoring human perception.</p><p>That misses the point by a mile.</p><p>Since we preach user experience, you'd expect these models to mirror how our minds work. Yet no framework nudges you to consider the user at every layer.</p><p>In this post, I&#8217;ll introduce a slight paradigm shift by mapping tokens with how our brains perceive digital interfaces.</p><p>The result isn&#8217;t an entirely novel token architecture but an evolution of existing 3-tier and 4-tier models into an experience&#8209;first framework.</p><p>The model has four main layers of abstraction for tokens, each serving a distinct purpose:</p><p><strong>Signals</strong>: <em>Simple tokens</em> that transmit discrete design information.<br><strong>Qualia</strong>: <em>Composite tokens</em> representing instances of subjective consciousness.<br><strong>Noemata</strong>: <em>Component tokens</em> as repeatable attributes of an experience.<br><strong>Strata</strong>: <em>Theme tokens</em> for the manifold horizon in which experiences occur.</p><p>Each layer corresponds to a stage in the chain from raw design stimulus to experienced interface all within the framework of the user&#8217;s perspective, mirroring the progression from observation to sensation to realization.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PtGp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PtGp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 424w, https://substackcdn.com/image/fetch/$s_!PtGp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 848w, https://substackcdn.com/image/fetch/$s_!PtGp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 1272w, https://substackcdn.com/image/fetch/$s_!PtGp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PtGp!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png" width="1100" height="344.5054945054945" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:456,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1100,&quot;bytes&quot;:60537,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!PtGp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 424w, https://substackcdn.com/image/fetch/$s_!PtGp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 848w, https://substackcdn.com/image/fetch/$s_!PtGp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 1272w, https://substackcdn.com/image/fetch/$s_!PtGp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0b2c19a-e482-409c-bae9-593836495c4b_1917x600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We&#8217;ll go over these layers in more detail further down, describing their role and how they relate to both implementation and perception, with examples of what tokens in that layer look like.</p><p>To get the most out of it, we first need to understand how design tokens and the methods we use to create them are built on prior explorations of human perception as forms of art.</p><h2>Why Perception Matters</h2><p>It&#8217;s already there, implicitly.</p><p>Design is as much about psychology as it is aesthetics. A <code>$primary-color</code> draws user attention, a <code>$spacing-L</code> suggests a comfortable margin, and so on.</p><p>When we tune color scales using harmonious intervals or scale typography with musical precision, we&#8217;re guiding the user&#8217;s eye in a way that reduces cognitive friction and builds trust.</p><p>We&#8217;re also ensuring some valuable consistency on our end. If you&#8217;ve ever wasted time trying to figure out why <code>--grey-150</code> exists when every other interval in every hue increments by 100, you know what I mean.</p><p>Colors, shapes, typography, and layout all work in concert to form a user's impression within milliseconds.</p><h3>From Art To Interface</h3><p>I studied a bit of art history in college for my Graphic Design major and can&#8217;t get enough of art movements and the philosophies behind them.</p><p>Soon after graduation I discovered Synchromism, a little-known short-lived early 20th-century movement that explored the relationship between color and music.</p><blockquote><p><em>&#8220;Before investigating the analogy of colour intervals it is necessary to emphasise the fact that the psychological equivalent of the simultaneous perception of two or more sounds, is the simultaneous perception of two or more juxtaposed colours. Consequently the analogue of harmony in sound must be sought in juxtaposed colours. Melody in colour will therefore result from the </em>espacement<em> of colours, their isolation by intervening neutral tint, and also from the order in which colours lie on the field of vision.&#8221;</em></p><p><em>&#8212; </em>Percival Tudor-Hart, &#8220;The Analogy of Sound and Color&#8221;, <em>Cambridge Magazine</em> 7, no. 21 (2 March 1918): 485</p></blockquote><h3>Synchromism In 30 Seconds</h3><p>Synchromism was an art movement founded by Stanton Macdonald-Wright and Morgan Russell.</p><p>From a desire to create form from color alone, they used musical scales as a framework to build paintings from pure hue relationships (called <em>synchromies</em>), defining &#8220;color scales&#8221; to orchestrate visual harmony just as a symphony arranges sound.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NlOA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NlOA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 424w, https://substackcdn.com/image/fetch/$s_!NlOA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 848w, https://substackcdn.com/image/fetch/$s_!NlOA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 1272w, https://substackcdn.com/image/fetch/$s_!NlOA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NlOA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp" width="500" height="478.94736842105266" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:546,&quot;width&quot;:570,&quot;resizeWidth&quot;:500,&quot;bytes&quot;:57468,&quot;alt&quot;:&quot;Stanton Macdonald-Wright, NATURE SYNCHROMY, 1924, oil on canvas, Metropolitan Museum of Art, New York&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Stanton Macdonald-Wright, NATURE SYNCHROMY, 1924, oil on canvas, Metropolitan Museum of Art, New York" title="Stanton Macdonald-Wright, NATURE SYNCHROMY, 1924, oil on canvas, Metropolitan Museum of Art, New York" srcset="https://substackcdn.com/image/fetch/$s_!NlOA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 424w, https://substackcdn.com/image/fetch/$s_!NlOA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 848w, https://substackcdn.com/image/fetch/$s_!NlOA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 1272w, https://substackcdn.com/image/fetch/$s_!NlOA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44f1faad-84be-4743-95f3-c7550b5a8012_570x546.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Pick one hue as your central &#8220;note&#8221;. Add the third and fifth tones from the color spectrum like a musical chord, placing them in nearby shapes.</p><p>Synchromies show waves of colors moving in and out, often converging into a vortex that bursts outward in complex harmonies.</p><p>Like a light show without lasers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tOBQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tOBQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 424w, https://substackcdn.com/image/fetch/$s_!tOBQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 848w, https://substackcdn.com/image/fetch/$s_!tOBQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 1272w, https://substackcdn.com/image/fetch/$s_!tOBQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tOBQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp" width="499" height="516.5415108451758" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1384,&quot;width&quot;:1337,&quot;resizeWidth&quot;:499,&quot;bytes&quot;:2091816,&quot;alt&quot;:&quot;Stanton Macdonald-Wright, Synchromy No.&#8239;3, 1917, oil on canvas, Brooklyn Museum, New York&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Stanton Macdonald-Wright, Synchromy No.&#8239;3, 1917, oil on canvas, Brooklyn Museum, New York" title="Stanton Macdonald-Wright, Synchromy No.&#8239;3, 1917, oil on canvas, Brooklyn Museum, New York" srcset="https://substackcdn.com/image/fetch/$s_!tOBQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 424w, https://substackcdn.com/image/fetch/$s_!tOBQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 848w, https://substackcdn.com/image/fetch/$s_!tOBQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 1272w, https://substackcdn.com/image/fetch/$s_!tOBQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f76e08-591f-4b9c-b325-c46dea18c6e7_1337x1384.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Imagine throwing ass to this&#8230;</figcaption></figure></div><p>Underpinning this methodology is a deep engagement with contemporary color science and music theory.</p><p>This rational basis aimed to deliver color that could be &#8220;heard&#8221; as well as seen.</p><h3>Influence in Design Systems</h3><p>Synchromism&#8217;s core insight (that harmonious aesthetic relationships can be composed like music) laid a conceptual foundation that guides modern design systems.</p><p>Though most prominent in color ramps and type scales, it can just as easily apply to spacing units, motion durations, and more.</p><h4>Color Scales</h4><p>Design systems often use what&#8217;s called a <em>nonatonic</em> (9-step) color scale from 100 to 900, with each interval 10% darker. An 18-step version denominated by 50 (5% darker) is <em>octodecatonic</em>.</p><p>These darkness intervals act like octaves, keeping each shade in harmony. HSL lightness shifts by a constant geometric ratio, like moving up scale degrees.</p><p>The terms <em>nonatonic</em> and <em>octodecatonic</em> come from musical scales and reference a stable central hue or &#8220;tonic&#8221; that usually sits at 500 or 450 (i.e. the median of the range).</p><p>Thankfully we don&#8217;t have to do all this by hand anymore, we can do it in CSS directly with <code>color-mix()</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!d4F2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!d4F2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 424w, https://substackcdn.com/image/fetch/$s_!d4F2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 848w, https://substackcdn.com/image/fetch/$s_!d4F2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 1272w, https://substackcdn.com/image/fetch/$s_!d4F2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!d4F2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png" width="1456" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:398014,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!d4F2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 424w, https://substackcdn.com/image/fetch/$s_!d4F2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 848w, https://substackcdn.com/image/fetch/$s_!d4F2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 1272w, https://substackcdn.com/image/fetch/$s_!d4F2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06a3770a-1c18-4e88-bf78-dc8d163c88da_2704x1492.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Natural tinting and shading isn&#8217;t as simple as the current color mixed with white and black respectively; it often blends in complementary colors and other colors of light around it.</p><p>A better approach is to choose a more dynamic range implemented like so:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RucO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RucO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 424w, https://substackcdn.com/image/fetch/$s_!RucO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 848w, https://substackcdn.com/image/fetch/$s_!RucO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 1272w, https://substackcdn.com/image/fetch/$s_!RucO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RucO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png" width="1456" height="842" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:842,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:422324,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RucO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 424w, https://substackcdn.com/image/fetch/$s_!RucO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 848w, https://substackcdn.com/image/fetch/$s_!RucO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 1272w, https://substackcdn.com/image/fetch/$s_!RucO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd0c19d1-d20d-42ec-aca0-c4df82e1fca3_2704x1564.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That said, we usually want to keep the tonic for fades:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1YBM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1YBM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 424w, https://substackcdn.com/image/fetch/$s_!1YBM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 848w, https://substackcdn.com/image/fetch/$s_!1YBM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 1272w, https://substackcdn.com/image/fetch/$s_!1YBM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1YBM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png" width="1456" height="1271" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1271,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:781499,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1YBM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 424w, https://substackcdn.com/image/fetch/$s_!1YBM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 848w, https://substackcdn.com/image/fetch/$s_!1YBM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 1272w, https://substackcdn.com/image/fetch/$s_!1YBM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff060c86d-2867-4c50-b21e-3b43d606a13a_2704x2360.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For JavaScript/TypeScript tokens, we can reproduce <code>color-mix()</code> with <a href="https://www.npmjs.com/package/chroma-js">Chroma.js</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MuK8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MuK8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 424w, https://substackcdn.com/image/fetch/$s_!MuK8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 848w, https://substackcdn.com/image/fetch/$s_!MuK8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 1272w, https://substackcdn.com/image/fetch/$s_!MuK8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MuK8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png" width="1456" height="1538" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1538,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:790943,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MuK8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 424w, https://substackcdn.com/image/fetch/$s_!MuK8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 848w, https://substackcdn.com/image/fetch/$s_!MuK8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 1272w, https://substackcdn.com/image/fetch/$s_!MuK8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3a920f4-0994-4a53-8508-721032c6e8da_2704x2856.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Typographic Scales</h4><p>If you aren&#8217;t already familiar with <a href="https://spencermortensen.com/articles/typographic-scale/">Spencer Mortensen&#8217;s breakdown of typographic scales</a>, give it a read. It&#8217;s a classic!</p><p>In short, he lays out the following formula for creating harmonious typographic scales:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f_i = f_0 r^{\\frac{i}{n}}&quot;,&quot;id&quot;:&quot;ZLQNLCUFRG&quot;}" data-component-name="LatexBlockToDOM"></div><p>His rationale is as straightforward as it gets so it&#8217;s worth repeat here:</p><blockquote><p><em>&#8220;A typographer chooses sizes from a typographic scale in the same way that a musician chooses notes from a musical scale.</em></p><p><em>Like a musical scale, a typographic scale is a </em>scale<em>, so it must obey the scaling property: if </em>f<em> is a size in the scale, then </em>rf<em> must also be a size in the scale, where </em>r<em> is the ratio of the scale.&#8221;</em></p></blockquote><p>Guess what? We can do this directly in CSS too!</p><p>The following gives us a <em>dodecatonic golden typographic scale</em> (12-step scale where the ratio is the golden ratio):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-P2s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-P2s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 424w, https://substackcdn.com/image/fetch/$s_!-P2s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 848w, https://substackcdn.com/image/fetch/$s_!-P2s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 1272w, https://substackcdn.com/image/fetch/$s_!-P2s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-P2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png" width="1456" height="1770" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1770,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1052010,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-P2s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 424w, https://substackcdn.com/image/fetch/$s_!-P2s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 848w, https://substackcdn.com/image/fetch/$s_!-P2s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 1272w, https://substackcdn.com/image/fetch/$s_!-P2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9a647c8-4232-476b-b13c-c6576257411e_2704x3288.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The tonic (<code>f&#8320;</code>) isn&#8217;t quite the median here but the scale works the same way.</p><p>We can tell by rendering each interval and visualizing an imaginary line at the edge or spaces in between. Notice how the line is curved for User Agent styling:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1PcA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1PcA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 424w, https://substackcdn.com/image/fetch/$s_!1PcA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 848w, https://substackcdn.com/image/fetch/$s_!1PcA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 1272w, https://substackcdn.com/image/fetch/$s_!1PcA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1PcA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png" width="1456" height="662" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:662,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:192607,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1PcA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 424w, https://substackcdn.com/image/fetch/$s_!1PcA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 848w, https://substackcdn.com/image/fetch/$s_!1PcA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 1272w, https://substackcdn.com/image/fetch/$s_!1PcA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0517de0e-3175-47eb-ae45-cf0f9ff1b3bc_1900x864.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When typography is scalar, the line is straight:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CflI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CflI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 424w, https://substackcdn.com/image/fetch/$s_!CflI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 848w, https://substackcdn.com/image/fetch/$s_!CflI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 1272w, https://substackcdn.com/image/fetch/$s_!CflI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CflI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png" width="1456" height="723" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:723,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:263430,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CflI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 424w, https://substackcdn.com/image/fetch/$s_!CflI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 848w, https://substackcdn.com/image/fetch/$s_!CflI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 1272w, https://substackcdn.com/image/fetch/$s_!CflI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc7d1cb3-3a30-4124-82f1-a1f748aa4c5d_1900x944.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Compositional Tokens</h2><p>Humans understand the world not by isolating individual elements, but by weaving them together into a coherent context.</p><p>In fact, the very word "context" comes from the Latin <em>contextus</em>, meaning "to weave together," highlighting that context is something actively constructed rather than a static backdrop.</p><p>Cognitive scientists note that context creation is an active, two-sided process; we continuously integrate cues from the environment and in turn our understanding of the context influences how we perceive those cues.</p><p>In other words, context and content shape each other in tandem, much like threads woven into a single fabric. At the neural level, the brain literally binds together features of an environment or experience to create a unique context representation.</p><p>Research in memory formation shows that the hippocampus quickly constructs a &#8220;conjunctive&#8221; representation of the various features of a place or situation.</p><p>This bound representation means that even a small subset of those features can later trigger recall of the whole context through pattern completion.</p><p>In cognitive terms, what we see or hear is heavily influenced by surrounding cues and our expectations.</p><p>The same visual or UI element can feel completely different in a different setting or theme. This underscores why designing for perception means designing for context: the human brain will always interpret interface signals through whatever contextual frame is in place.</p><h3>Signals</h3><p>Generally speaking, a signal is a transmission of information from one part of a system to another. In design systems, they provide a finite palette of options from which more complex experiences emerge.</p><p>By thinking about them in terms of constraints, we enforce a baseline harmony and limit the design space to a controlled and ubiquitous vocabulary.</p><h4>Wait- Aren&#8217;t Qualia Irreducible?</h4><p>Philosophically, <em>qualia</em> refer to the subjective, irreducible experience of consciousness.</p><p>Whether or not irreducibility is true depends on if subjective consciousness fully emerges from the objective processes of the brain.</p><p>Regardless, it&#8217;s undoubtedly true that sensory experience involves signals transmitted through the nervous system. Our brains receive and process these signals to construct our perception of the world around us.</p><p>Given this, we can make an argument either way such that this paradigm becomes flexible enough for both 3-tier and 4-tier token architectures. In a 3-tier token architecture, signals simply become the most granular qualia.</p><h4>Constraints</h4><ul><li><p>Signal tokens are either discrete raw values or use other signals to compute a new signal.</p></li><li><p>1 token = 1 value</p></li></ul><h4>Examples</h4><p>Signals should be used whenever you need a concrete value.</p><p>If you plan on computing scalar values for color and typography like we do above, it&#8217;s a good idea to have a set of core signals that include some of the terms and ratios mentioned earlier:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m5XO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m5XO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 424w, https://substackcdn.com/image/fetch/$s_!m5XO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 848w, https://substackcdn.com/image/fetch/$s_!m5XO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 1272w, https://substackcdn.com/image/fetch/$s_!m5XO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m5XO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png" width="1456" height="1038" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1038,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:306009,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!m5XO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 424w, https://substackcdn.com/image/fetch/$s_!m5XO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 848w, https://substackcdn.com/image/fetch/$s_!m5XO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 1272w, https://substackcdn.com/image/fetch/$s_!m5XO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdda1f23f-b0c5-43cb-aa2c-47c1bba89773_2704x1928.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Signals can also be values for other things like breakpoints, spacing, border radii, shadows, and so on:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WKLK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WKLK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 424w, https://substackcdn.com/image/fetch/$s_!WKLK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 848w, https://substackcdn.com/image/fetch/$s_!WKLK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 1272w, https://substackcdn.com/image/fetch/$s_!WKLK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WKLK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png" width="1456" height="694" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:694,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:222932,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WKLK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 424w, https://substackcdn.com/image/fetch/$s_!WKLK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 848w, https://substackcdn.com/image/fetch/$s_!WKLK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 1272w, https://substackcdn.com/image/fetch/$s_!WKLK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f7e587b-24bd-4305-b626-b0bc1007161b_2704x1288.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Naming</h4><p>Since signals are often part of a scale or compound value, they&#8217;re usually named relative to similar signals (e.g. <code>$red-400</code> vs <code>$red-500</code> or <code>$dark-red</code> vs <code>$light-red</code>) within the same group. Numbers should provide consistency across intervals:</p><blockquote><p>&#9989; <strong>GOOD</strong>: <code>$red-fade-90</code>, <code>$red-fade-80<br></code>&#10060; <strong>BAD</strong>: <code>$faded-red</code>, <code>$more-faded-red</code></p></blockquote><div><hr></div><h3>Qualia</h3><p>The term <em>qualia</em> (singular: <em>quale</em>, pronounced &#8220;kwol-ay&#8221;) comes from <a href="https://iep.utm.edu/sense-da/">sense-data theory</a>, referring to the subjective quality of conscious experience as properties of sense-data themselves.</p><p>When we see a button on a screen, properties like &#8220;red color, 10pt font, 4px radius&#8221; <a href="https://en.wikipedia.org/wiki/Feature_integration_theory?utm_source=blog.robhameetman.com">are parsed pre-attentively and then combined</a>, almost like a form of sensory data compression.</p><p>What we actually experience is the overall look and feel as a prominent call to action.</p><h4>Make It Make Sense</h4><p>Imagine trying to describe what it feels like to see the color red to someone born totally blind.</p><p>Qualia are these immediate sensations. They capture &#8220;what it&#8217;s like&#8221; to experience a signal with meaningful context.</p><p>In reality, we don&#8217;t just run into the color red. Rather, we experience the &#8220;redness&#8221; of something like an apple or a can of Coca-Cola or a button on a screen.</p><p>As qualia, the redness of an apple isn&#8217;t equivalent to the redness of a can of Coca-Cola even if the two hues are exactly the same.</p><p>If signals are ingredients, qualia are flavors. They encourage us to start thinking in terms of low-level sensations rather than objective values.</p><h4>Constraints</h4><ul><li><p>Qualia may use raw values, signals, and other qualia.</p></li><li><p>They should never use noemata or strata.</p></li></ul><h4>Examples</h4><p>Qualia typically use signals either directly or in composite values to create new tokens with specialized meaning.</p><p>For example, a color scale might have signals for blue like <code>$blue-100</code> used by qualia like <code>$primary-color</code> or <code>$info-color</code>.</p><p>Consider our typographic scale from earlier.</p><p>The scalar tokens <code>&#8212;-f12</code>, <code>&#8212;-f11</code>, <code>&#8212;-f10</code>, etc. are signals, while the heading tokens <code>&#8212;-h1</code>, <code>&#8212;-h2</code>, <code>&#8212;-h3</code>, etc. are qualia. Both types are used for font sizes, but the latter have specialized meaning as heading sizes.</p><p>These tokens can then compose more complex qualia for the headings as CSS <code>font</code> shorthands:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X-BL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X-BL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 424w, https://substackcdn.com/image/fetch/$s_!X-BL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 848w, https://substackcdn.com/image/fetch/$s_!X-BL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 1272w, https://substackcdn.com/image/fetch/$s_!X-BL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!X-BL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png" width="1456" height="422" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:422,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:181676,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!X-BL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 424w, https://substackcdn.com/image/fetch/$s_!X-BL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 848w, https://substackcdn.com/image/fetch/$s_!X-BL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 1272w, https://substackcdn.com/image/fetch/$s_!X-BL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15a598b0-e125-425d-8829-4881ae05cb4f_3420x992.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>By packaging decisions in this way, qualia also help reduce errors. Use theme wherever you want to encapsulate a design decision with a purpose.</p><p>As a rule of thumb, if a style decision would appear in CSS as a shorthand or a group of properties, it&#8217;s a good candidate for a quale token.</p><h4>Naming</h4><p>Quale token names should capture meaning or usage. A good practice is to think, <em>&#8220;How would I describe this token&#8217;s role to a colleague or stakeholder?&#8221;</em></p><p>For example, when naming and grouping color qualia, consider the underlying meaning. Words like &#8220;primary&#8221;, &#8220;secondary&#8221;, &#8220;tertiary&#8221; are about <em>priority</em>; system colors indicate <em>intent</em>:</p><blockquote><p>&#10060; <strong>BAD</strong>: <code>$red</code>, <code>$grey</code><br>&#9989; <strong>GOOD</strong>: <code>$primary-color</code>, <code>$secondary-color</code><br>&#9989; <strong>GOOD</strong>: <code>$error-color</code>, <code>$disabled-color</code></p></blockquote><div><hr></div><h3>Noemata</h3><p>In phenomenological terms, noemata (singular: noema) refer to the content of thought; essentially, something as it is experienced or intended in the mind.</p><p>This ensures that each component&#8217;s design supports its function.</p><h4>Noematic Nuance</h4><p>Conceptually, noemata overlap with the Platonic idea of <em>eidos, </em>the essence of a thing&#8212;it&#8217;s &#8220;thingness&#8221;, if you will.</p><p>In programming terms, eidos is like an <code>interface</code> implemented by a <code>class</code>. It&#8217;s the <em>mental contract</em> that determines how language shapes reality.</p><p>Building on our red apple example from earlier, let&#8217;s break down the requirements for some eidetic analysis:</p><ul><li><p>it must be an apple</p></li><li><p>it must be red (mostly)</p></li></ul><p>The word &#8220;mostly&#8221; here is the key to understanding the nuance between eidos and noemata.</p><p>Say we have two red apples. One has a small spot of discoloration; the other has no perceivable discoloration. Both are red enough to be red apples but you probably think of one as fresher than the other.</p><p>In order to account for freshness at the eidos level, we have to increase language specificity from &#8220;red apple&#8221; to something like &#8220;fresh red apple&#8221; or &#8220;entirely red apple&#8221;, which narrows the noematic scope.</p><p>If we have two fresh (entirely) red apples, we can still perceive one as fresher than the other but now we need more sensory information like texture, firmness, maybe even aroma or taste.</p><p>Both eidos and noemata represent ideal forms. The nuance is that eidos defines what makes a thing that thing while noemata are the repeatable attributes of an experience of that thing.</p><h4>Constraints</h4><ul><li><p>Noemata may use any tokens from any other layer.</p></li><li><p>In some cases they may also be another noema token.</p></li><li><p>In rare cases they may be one-off values.</p></li></ul><h4>Examples</h4><p>Use noemata tokens to organize design decisions by component. This way, if a particular component needs to be reskinned or adjusted, you can tweak its tokens without affecting others.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AJcP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AJcP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 424w, https://substackcdn.com/image/fetch/$s_!AJcP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 848w, https://substackcdn.com/image/fetch/$s_!AJcP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 1272w, https://substackcdn.com/image/fetch/$s_!AJcP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AJcP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png" width="1456" height="386" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39762360-1d76-413f-b684-76d54d098240_2940x780.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:386,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:116333,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AJcP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 424w, https://substackcdn.com/image/fetch/$s_!AJcP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 848w, https://substackcdn.com/image/fetch/$s_!AJcP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 1272w, https://substackcdn.com/image/fetch/$s_!AJcP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39762360-1d76-413f-b684-76d54d098240_2940x780.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Noemata sometimes reference other noemata when it makes sense, though this usually happens intramodularly:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GhaB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GhaB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 424w, https://substackcdn.com/image/fetch/$s_!GhaB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 848w, https://substackcdn.com/image/fetch/$s_!GhaB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 1272w, https://substackcdn.com/image/fetch/$s_!GhaB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GhaB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png" width="1456" height="456" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/81afd497-3d32-4060-837f-4e195902e276_2940x920.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:456,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:145453,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GhaB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 424w, https://substackcdn.com/image/fetch/$s_!GhaB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 848w, https://substackcdn.com/image/fetch/$s_!GhaB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 1272w, https://substackcdn.com/image/fetch/$s_!GhaB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81afd497-3d32-4060-837f-4e195902e276_2940x920.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>From time to time, it may be appropriate to reference noemata from other components:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JcGV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JcGV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 424w, https://substackcdn.com/image/fetch/$s_!JcGV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 848w, https://substackcdn.com/image/fetch/$s_!JcGV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 1272w, https://substackcdn.com/image/fetch/$s_!JcGV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JcGV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png" width="1456" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:128184,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JcGV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 424w, https://substackcdn.com/image/fetch/$s_!JcGV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 848w, https://substackcdn.com/image/fetch/$s_!JcGV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 1272w, https://substackcdn.com/image/fetch/$s_!JcGV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8475a33c-d82f-4a36-9b29-294a8e0e8058_2940x848.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Naming</h4><p>Noema token names usually prefix the component or pattern name to group them:</p><blockquote><p>&#10060; <strong>BAD</strong>: <code>$color-background-dropdown</code><br>&#9989; <strong>GOOD</strong>: <code>$dropdown-color</code></p></blockquote><p>This way, anyone looking at the token name knows which component it belongs to and what aspect it controls.</p><p>It&#8217;s also helpful to mirror the component&#8217;s parts/states in the token names. When it comes to buttons and links specifically, we recommend the term &#8220;engaged&#8221; when pressed and hover states overlap instead of words like &#8220;active&#8221;. <code>:active</code> is a CSS pseudo-class and misusing this term can cause confusion.</p><p>By naming noemata tokens clearly, we capture the nuance of each component&#8217;s design in a way that is immediately understandable.</p><div><hr></div><h3>Strata</h3><p>The contextual background or frame in which the user&#8217;s experience occurs is called their <em>horizon</em>.</p><p>A horizon links past experiences together and, in those connections, guides which new ones join the series. Those links then shape our overarching sense of reality.</p><p>The user&#8217;s horizon includes not just what is explicitly present in the viewport, but also the implied possibilities and potential experiences. The interstitial links of these experiences are the theme.</p><p>A theme is essentially a configuration.</p><p>Strata (singular: stratum), therefore, are the layers that configure the user&#8217;s horizon of experience.</p><h4>Constraints</h4><ul><li><p>Strata may use qualia and signal tokens.</p></li><li><p>They may also reference another stratum token.</p></li><li><p>They should never use noemata.</p></li><li><p>In rare cases they may be one-off values.</p></li></ul><h4>Examples</h4><p>Use strata to enable theming at scale.</p><p>While strata could be limited to specific pages or views, a good rule of thumb is to keep these tokens relevant to design elements used anywhere in the app:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7dcn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7dcn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 424w, https://substackcdn.com/image/fetch/$s_!7dcn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 848w, https://substackcdn.com/image/fetch/$s_!7dcn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 1272w, https://substackcdn.com/image/fetch/$s_!7dcn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7dcn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png" width="1456" height="598" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:598,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:235297,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7dcn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 424w, https://substackcdn.com/image/fetch/$s_!7dcn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 848w, https://substackcdn.com/image/fetch/$s_!7dcn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 1272w, https://substackcdn.com/image/fetch/$s_!7dcn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f863890-a17a-42a0-8237-f0c6469588c6_2940x1208.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>They can also encapsulate categories of components:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AZBO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AZBO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 424w, https://substackcdn.com/image/fetch/$s_!AZBO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 848w, https://substackcdn.com/image/fetch/$s_!AZBO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 1272w, https://substackcdn.com/image/fetch/$s_!AZBO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AZBO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png" width="1456" height="632" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:632,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:252222,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/168566184?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AZBO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 424w, https://substackcdn.com/image/fetch/$s_!AZBO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 848w, https://substackcdn.com/image/fetch/$s_!AZBO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 1272w, https://substackcdn.com/image/fetch/$s_!AZBO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d687da7-75e4-4c9b-b59b-8a6aea37b000_2940x1276.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As strata these tokens tell us that the system doesn&#8217;t have <code>&lt;Form&gt;</code> component; if it does, they become noemata.</p><h4>Naming</h4><p>Strata shouldn&#8217;t &#8220;know&#8221; the theme they&#8217;re in, meaning names should be theme-agnostic.</p><blockquote><p>&#10060; <strong>BAD</strong>: <code>$theme-brand-text-color</code><br>&#9989; <strong>GOOD</strong>: <code>$text-color</code></p></blockquote><div><hr></div><h2>Component Categorization</h2><p>Beyond token architecture, a way to further align design systems with human understanding is to categorize UI components by their executive function.</p><p>For consistency, I&#8217;m sticking with words that end in &#8220;ation&#8221; but the taxonomy of executive function itself isn't a strict science. Teams should feel free to modify these categories and use different terms if it better suits their context.</p><p>Many UI components can serve multiple purposes, but this approach provides a useful lens for thinking about design intent.</p><p>For instance:</p><ul><li><p><strong>Actuation/Activation</strong> - Components that cause something to become active or effective, or simply to "turn on":</p><ul><li><p><code>&lt;Button&gt;</code>/<code>&lt;Button.Group&gt;</code></p></li><li><p><code>&lt;Checkbox&gt;</code>/<code>&lt;Checkbox.Group&gt;</code></p></li><li><p><code>&lt;Radio&gt;</code>/<code>&lt;Radio.Group&gt;</code></p></li><li><p><code>&lt;Toggle&gt;</code></p></li></ul></li><li><p><strong>Adaptation</strong> - Components that allow the user experience to change or be changed in accordance with various edge cases:</p><ul><li><p><code>&lt;Autocomplete&gt;</code></p></li><li><p><code>&lt;CatchError&gt;</code>/<code>&lt;ErrorBoundary&gt;</code></p></li><li><p><code>&lt;ErrorPage&gt;</code></p></li><li><p><code>&lt;Loading&gt;</code></p></li><li><p><code>&lt;LanguageSelector&gt;</code></p></li><li><p><code>&lt;Image&gt;</code></p></li><li><p><code>&lt;NoResults&gt;</code></p></li><li><p><code>&lt;ThemeSwitcher&gt;</code></p></li><li><p><code>&lt;Skeleton&gt;</code></p></li><li><p><code>&lt;Video&gt;</code></p></li></ul></li><li><p><strong>Classification</strong> - Components that label, categorize, or filter content:</p><ul><li><p><code>&lt;Avatar&gt;</code>/<code>&lt;Avatar.Group&gt;</code></p></li><li><p><code>&lt;Chip&gt;</code> or <code>&lt;Pill&gt;</code></p></li><li><p><code>&lt;Label&gt;</code></p></li><li><p><code>&lt;Search&gt;</code></p></li><li><p><code>&lt;Tag&gt;</code></p></li></ul></li><li><p><strong>Explication</strong> - Components that explain or assist user understanding of the interface or content:</p><ul><li><p><code>&lt;BarChart&gt;</code></p></li><li><p><code>&lt;GeoChart&gt;</code></p></li><li><p><code>&lt;Histogram&gt;</code></p></li><li><p><code>&lt;Popover&gt;</code></p></li><li><p><code>&lt;Tooltip&gt;</code></p></li><li><p><code>&lt;TreeMap&gt;</code></p></li></ul></li><li><p><strong>Narration</strong> - Components that present content or guide through a temporal flow:</p><ul><li><p><code>&lt;Fade&gt;</code></p></li><li><p><code>&lt;ProgressBar&gt;</code></p></li><li><p><code>&lt;SlideIn&gt;</code>/<code>&lt;SlideOut&gt;</code></p></li><li><p><code>&lt;Stepper&gt;</code></p></li><li><p><code>&lt;Timeline&gt;</code></p></li></ul></li><li><p><strong>Organization</strong> - Components that bring content together to help users identify information and/or maintain consistency:</p><ul><li><p><code>&lt;Article&gt;</code></p></li><li><p><code>&lt;Carousel&gt;</code></p></li><li><p><code>&lt;Dashboard&gt;</code></p></li><li><p><code>&lt;Grid&gt;</code></p></li><li><p><code>&lt;Menu&gt;</code></p></li><li><p><code>&lt;Pagination&gt;</code></p></li><li><p><code>&lt;Select&gt;</code> or <code>&lt;Dropdown&gt;</code></p></li><li><p><code>&lt;Tabs&gt;</code></p></li><li><p><code>&lt;Table&gt;/&lt;DataGrid&gt;</code></p></li></ul></li><li><p><strong>Orientation</strong> - Components that help users navigate across pages and views:</p><ul><li><p><code>&lt;Breadcrumbs&gt;</code></p></li><li><p><code>&lt;Footer&gt;</code></p></li><li><p><code>&lt;Header&gt;</code></p></li><li><p><code>&lt;NavBar&gt;</code></p></li><li><p><code>&lt;Sidebar&gt;</code></p></li><li><p><code>&lt;Sitemap&gt;</code></p></li></ul></li><li><p><strong>Prioritization</strong> - Components that highlight or emphasize important information to grab attention:</p><ul><li><p><code>&lt;Alert&gt;</code></p></li><li><p><code>&lt;Badge&gt;</code></p></li><li><p><code>&lt;Banner&gt;</code></p></li><li><p><code>&lt;Callout&gt;</code></p></li><li><p><code>&lt;Drawer&gt;</code></p></li><li><p><code>&lt;Hero&gt;</code></p></li><li><p><code>&lt;Modal&gt;</code></p></li><li><p><code>&lt;Notification&gt;</code>/<code>&lt;Toast&gt;</code></p></li><li><p><code>&lt;Snackbar&gt;</code></p></li></ul></li><li><p><strong>Segmentation</strong> - Components that break or structure content into digestible sections:</p><ul><li><p><code>&lt;Accordion&gt;</code></p></li><li><p><code>&lt;Box&gt;</code></p></li><li><p><code>&lt;Card&gt;</code></p></li><li><p><code>&lt;Divider&gt;</code></p></li><li><p><code>&lt;List&gt;</code></p></li><li><p><code>&lt;Item&gt;</code></p></li><li><p><code>&lt;Page&gt;</code></p></li><li><p><code>&lt;Section&gt;</code></p></li></ul></li></ul><p>On the surface, this approach might not appear to offer much more than traditional affordance-based component classifications. The main advantage of naming groups by the job they do is that conversations align to outcomes instead of surface form (&#8220;is this a button or a link?&#8221;).</p><p>You can plan to &#8220;improve actuation clarity across checkout&#8221; or &#8220;harden adaptation in low-connectivity scenarios,&#8221; then ship coordinated changes across multiple components with shared tokens and guidelines.</p><p>Specs can state, &#8220;This screen uses actuation and prioritization patterns,&#8221; which immediately maps to known tokens, behaviors, and even accessibility rules. Designers choose which function; engineers apply the corresponding patterns.</p><p>Instrumentation can also roll up by function: activation rate, time-to-orient, attention capture vs. dismissal, etc. Product decisions become &#8220;our prioritization patterns underperform on mobile,&#8221; not &#8220;this one toast is weird.&#8221;</p><p>Further, components like <code>&lt;ErrorBoundary&gt;</code>, <code>&lt;Image&gt;</code>, <code>&lt;Video&gt;</code>, <code>&lt;Skeleton&gt;</code>, etc. tend to be disregarded by Product as "utilities"; grouping them under <em>Adaptation</em> clarifies their actual value.</p><p>Likewise, <code>&lt;Menu&gt;</code> and <code>&lt;List&gt;</code> are both typically considered structural components in most design systems, and Product Developers often don&#8217;t understand the difference. This approach clarifies that a <code>&lt;List&gt;</code> is a method of segmentation while a <code>&lt;Menu&gt;</code> is more about organization.</p><p>The goal is to encourage thinking about why a component exists and to organize your library in a way that reflects those purposes. This can help ensure coverage and guide designers on what component to use for a given problem.</p><h2>Final Thoughts</h2><p>Embracing the construction of human perception as a guiding concept leads to a token architecture that is flexible, scalable, and truly user-centric.</p><p>By mirroring how humans construct and perceive contexts through meaningful combinations and nuanced variations, we create scalable design systems that feel naturally adaptive.</p><p>The user&#8217;s experience will be distinct where it matters, yet comfortably familiar underneath, because we&#8217;ve engineered our tokens the way the mind itself works: composing new experiences out of well-understood pieces, artfully woven together.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Up Next</h2><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;fb650cd6-0685-41f9-9e1c-9a59f0211c2f&quot;,&quot;caption&quot;:&quot;Icons are everywhere, yet they&#8217;re probably the easiest thing in your design system to screw up. I&#8217;ve seen so many <Icon> components that overcomplicate the DOM while doing nothing to make accessibility easier to achieve.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[WIP] Accessible SVG Icons The Right Way&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I build Design Systems and Micro-frontends in TypeScript and React with a focus on developer experience / Staff Frontend Engineer / TED curator / Ex-Apple (HIG) / 1.2M+ subs&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-24T22:17:59.979Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c54b3810-d9fa-4986-9775-77008fb03c90_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/accessible-svg-icons&quot;,&quot;section_name&quot;:&quot;Resources&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:166355744,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;90ce0459-c10b-4761-a73d-237628d0b045&quot;,&quot;caption&quot;:&quot;Feature teams move slower than they should. What takes a week could take a day. Simple changes somehow break unrelated things. New hires need months to become useful.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[WIP] Building a Pit of Success with Inductive Reasoning&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I&#8217;m a Staff Design Engineer who builds sexy, scalable enterprise-strength design systems.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-24T22:01:39.994Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!v3zD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/building-a-pit-of-success&quot;,&quot;section_name&quot;:&quot;Deeps&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:166478385,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5bd8eae0-5271-4e31-9303-0c7392e3f0e3&quot;,&quot;caption&quot;:&quot;&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[WIP] Mode-Aware Tokens with OKLCH&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I build Design Systems and Micro-frontends in TypeScript and React with a focus on developer experience / Staff Frontend Engineer / TED curator / Ex-Apple (HIG) / 1.2M+ subs&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-24T22:30:37.254Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!NYv-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/wip-mode-aware-tokens-with-oklch&quot;,&quot;section_name&quot;:&quot;Resources&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:171579490,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Any thoughts on what else you&#8217;d like to see? Leave a comment or hop into chat and let me know!</p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/design-systems-for-human-perception?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/design-systems-for-human-perception?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>If you enjoyed this post, please subscribe, hit the &#10084;&#65039; button, and share/restack &#128257; it with others who might find it helpful!</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[[WIP] Mode-Aware Tokens with OKLCH]]></title><description><![CDATA[Making the most of the browser's color rendering.]]></description><link>https://blog.robhameetman.com/p/wip-mode-aware-tokens-with-oklch</link><guid isPermaLink="false">https://blog.robhameetman.com/p/wip-mode-aware-tokens-with-oklch</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Sun, 24 Aug 2025 22:30:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NYv-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NYv-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NYv-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!NYv-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!NYv-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!NYv-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NYv-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1909332,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/171579490?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NYv-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!NYv-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!NYv-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!NYv-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55ac3ddc-ddc7-4527-843b-20f73583d2a2_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="https://blog.robhameetman.com/p/wip-mode-aware-tokens-with-oklch">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[[WIP] Accessible SVG Icons The Right Way]]></title><description><![CDATA[Icons are everywhere, yet they&#8217;re probably the easiest thing in your design system to screw up.]]></description><link>https://blog.robhameetman.com/p/accessible-svg-icons</link><guid isPermaLink="false">https://blog.robhameetman.com/p/accessible-svg-icons</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Sun, 24 Aug 2025 22:17:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c54b3810-d9fa-4986-9775-77008fb03c90_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!enWJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!enWJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!enWJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!enWJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!enWJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!enWJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:577250,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/166355744?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!enWJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!enWJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!enWJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!enWJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0f6fa-7f74-4bf4-9938-44cc971c0623_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Icons are everywhere, yet they&#8217;re probably the easiest thing in your design system to screw up. I&#8217;ve seen so many <code>&lt;Icon&gt;</code> components that overcomplicate the DOM while doing nothing to make accessibility easier to achieve.</p><p>If your <code>&lt;Icon&gt;</code> component wraps your <code>&lt;svg&gt;</code> element in a <code>&lt;div&gt;</code>- especially if that <code>&lt;div&gt;</code> has no classes- you&#8217;re already headed down the wrong path.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>I try to avoid having an <code>&lt;Icon&gt;</code> component at all, though there are some approaches that justify it- like using a custom icon font. We&#8217;ll dive into this later, but the prevalence of icon fonts like FontAwesome hints at the key to keeping icons dead simple: treating them like text.</p><h2>Treating Icons Like Text</h2><p>Icons should behave like text because they're fundamentally semantic content used to convey meaning. They&#8217;re usually inline elements that flow with surrounding text, which means they share the following traits:</p><ul><li><p><strong>Context</strong>: Like words, icons represent concepts, actions, or information states. The rule of thumb with buttons, especially in headers or sidebars for navigation, is to either show the user with an icon or tell the user with a label but not both.</p></li><li><p><strong>Scalability</strong>: Icons should scale conformally with text when users adjust font sizes.</p></li><li><p><strong>Color</strong>: Icons typically inherit the color of surrounding text for visual consistency.</p></li><li><p><strong>Alignment</strong>: Icons need to align properly with text baselines and line heights.</p></li></ul><p>Leveraging the browser&#8217;s ability to render text by setting the <code>&lt;svg&gt;</code> element&#8217;s <code>fill</code> attribute to <code>"currentColor"</code> and using <code>em</code> instead of <code>rem</code> for sizing allows these behaviors to emerge organically.</p><h2>Recommended Props</h2>
      <p>
          <a href="https://blog.robhameetman.com/p/accessible-svg-icons">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[[WIP] Building a Pit of Success with Inductive Reasoning]]></title><description><![CDATA[Feature teams move slower than they should.]]></description><link>https://blog.robhameetman.com/p/building-a-pit-of-success</link><guid isPermaLink="false">https://blog.robhameetman.com/p/building-a-pit-of-success</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Sun, 24 Aug 2025 22:01:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!v3zD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!v3zD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!v3zD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!v3zD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!v3zD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!v3zD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!v3zD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1467192,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/166478385?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!v3zD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!v3zD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!v3zD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!v3zD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Feature teams move slower than they should. What takes a week could take a day. Simple changes somehow break unrelated things. New hires need months to become useful.</p><p>This isn't a people problem. It's a systems problem.</p><p>The root cause is excessive cognitive load. It's the mental weight developers carry to understand your code, your patterns, and your unique way of doing things. This load is the silent killer of velocity and quality.</p><p>Most advice on this focuses on cleaning up code. That's only a third of the battle. You can't just fix the symptoms. You need a strategy to fix the environment.</p><p>This article gives you that strategy. First, we'll unpack the complete cognitive load framework. Then, we'll reveal the most powerful tool to reduce it: engineering for inductive reasoning.</p><p>Your goal is to build a system where the right way to do things is the easiest way to discover. This is how you turn a team of good engineers into a high-performance engine.</p><h2><strong>Understanding Cognitive Load (Completely)</strong></h2><p>If you&#8217;ve read <a href="https://github.com/zakirullin/cognitive-load">Cognitive Load Is What Matters</a> on Github, then you&#8217;re already ahead of the curve. This is a great primer but only covers two types of mental overhead.</p><p>In software, we deal with three specific types of load. You must understand all three to fix the problem.</p><p>Here&#8217;s a more complete overview of how mental overhead is categorized according to <em>cognitive load theory</em>:</p><ul><li><p><strong>Intrinsic</strong><br>Overhead associated directly with a topic- i.e. the code, the problem, the business logic, and so on. Essentially, this is what comes to mind when we think of complexity. Spaghetti code overloads working memory, making it more difficult to do our work.</p></li><li><p><strong>Germane</strong><br>Overhead required to create a permanent store of knowledge or &#8220;schema&#8221;. In cognitive psychology, a <em>schema</em> is a mental model of how everything fits together. Thus, germane overhead is the effort required to build up recognition- which works faster than recall.</p></li><li><p><strong>Extraneous</strong><br>Overhead added by how information is presented. It comes from how we write and document code, not what the code does. It's the mental energy spent deciphering a bad variable name or untangling a nested conditional.</p></li></ul><p>Germane overhead is about building recognition but it&#8217;s rooted in the perceiver&#8217;s level of effort, whereas intrinsic overhead is about information itself and extraneous overhead is about its source.</p><p>These three types of mental overhead are not separate. They work together.</p><p>A complex task (high intrinsic load) becomes impossible when buried in messy code (high extraneous load). Both are made worse if the system itself becomes a niche skill that new hires must master (high germane load).</p><p>Your job is to manage the entire system. Ignoring one type just shifts the burden elsewhere. You need a tool that addresses all three at once.</p>
      <p>
          <a href="https://blog.robhameetman.com/p/building-a-pit-of-success">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[How I Built CSS's light-dark() in TypeScript]]></title><description><![CDATA[Leveraging Modern JavaScript to Mimic CSS Behavior Without Headaches]]></description><link>https://blog.robhameetman.com/p/how-i-built-csss-light-dark-in-typescript</link><guid isPermaLink="false">https://blog.robhameetman.com/p/how-i-built-csss-light-dark-in-typescript</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Mon, 04 Aug 2025 08:52:31 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3b40bed0-7772-4406-997b-9b0d33d77e87_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wUS6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wUS6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!wUS6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!wUS6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!wUS6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wUS6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1140746,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/170046011?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wUS6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!wUS6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!wUS6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!wUS6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F912ebe0c-4d91-4d4e-b53e-c16766aa8124_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This weekend I learned about CSS's <code>light-dark()</code> function, which automatically adapts colors to user preferences without JavaScript.</p><p>Naturally, I wondered if we can recreate this behavior in TypeScript.</p><p>The challenge was deceptively complex because I wanted a solution that could handle live updates while staying memory-safe.</p><p>In this article, I'll show you how I achieved the desired behavior in tokens that maintain string compatibility while responding to system changes automatically.</p><h2>Why It Matters</h2><p>Design tokens are format-agnostic values that create a shared visual language between designers and engineers. Being format-agnostic means they transcend specific implementations like CSS or TypeScript.</p><p>Keeping the underlying logic consistent across these formats gives us three key advantages:</p><ul><li><p><strong>Truthiness</strong><br>Tokens reflect actual design decisions, not CSS/TypeScript tokens or Figma variables, which eliminates interpretation gaps.</p></li><li><p><strong>Consistency</strong><br>No drift/errata between systems or hex value mismatches in different files.</p></li><li><p><strong>Resilience<br></strong>Update a token in one place and it propagates everywhere.</p></li></ul><p>These three cover the critical needs: accuracy, reliability, and adaptability.</p><h2>The Basic Implementation</h2><p>We could easily implement this in TypeScript just by checking the <code>(prefers-color-scheme: dark)</code> query:</p><pre><code>export const lightDark = (light: string, dark: string) =&gt; {
  const query = globalThis.matchMedia('(prefers-color-scheme: dark)');
  const isDark = query?.matches || false;

  return isDark ? dark : light;
};</code></pre><p>The problem is that we&#8217;ll be passing around these values in the <code>theme</code> object.</p><p>The function above returns a value once, which means we would need to invoke it whenever we want to get the current value of that token.</p><h2>More Advanced Requirements</h2><p>In CSS, our theme tokens are defined like this:</p><pre><code>--app-background-color: light-dark(var(--slate-100), var(--slate-900));
--text-color: light-dark(var(--slate-900), var(--slate-100));</code></pre><p>That means that in TypeScript, our theme object needs to be instantiated likewise:</p><pre><code>export interface Theme {
  readonly appBackgroundColor: string;
  readonly textColor: string;
}

export const THEME = Object.freeze&lt;Theme&gt;({
  appBackgroundColor: lightDark(SLATE[100], SLATE[900]),
  textColor: lightDark(SLATE[900], SLATE[100]),
});</code></pre><p>This adds the following requirements to our implementation:</p><ul><li><p>Values returned by <code>lightDark()</code> must be <strong>auto-updated</strong> when the system theme is changed.</p></li><li><p>This solution must not result in <strong>memory</strong> leaks. Basically, if we use event listeners we need to also use something like an <code>AbortController</code> to remove it if/when our <code>theme</code> object is cleaned up by garbage collection.</p></li><li><p><strong>Performance</strong> should be non-blocking. We need to keep in mind our core web vitals; a bad approach could degrade INP and CLS scores.</p></li><li><p><strong>Syntax</strong> and <strong>typing </strong>should be straightforward enough that this solution results in an intuitive developer experience.</p></li></ul><p>In order for values to be auto-updated while maintaining the best syntax and typing, values returned by lightDark() must be string-like.</p><p>What this means is that the value must behave like a string when we pass it around, especially when we interpolate it in template strings (which is important for CSS-in-JS).</p><p>However, since everything in JavaScript is an object, the value can also have a prototype which is a subtype of String and/or use custom properties/methods that regular strings don&#8217;t have.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#10084;&#65039; <em>Like what you&#8217;re seeing so far?</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>Possible Approaches</h2><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{array}{@{}lccccc@{}}\n  \\textbf{Approach}       &amp; \\textbf{Auto-Update}      &amp; \\textbf{Memory}  &amp; \\textbf{Syntax} &amp; \\textbf{Perf}           &amp; \\textbf{Typing}\\\\\n  \\hline\n  \\text{Proxy}            &amp; \\checkmark                &amp; \\checkmark              &amp; \\text{Good}     &amp; \\checkmark    &amp; \\text{Fair}\\\\\n  \\text{Per-Value Getters}&amp; \\checkmark                &amp; \\checkmark              &amp; \\text{Best}     &amp; \\checkmark              &amp; \\text{Best}\\\\\n  \\text{Value Objects}    &amp; \\times                    &amp; \\checkmark              &amp; \\text{Poor}     &amp; \\checkmark\\checkmark    &amp; \\text{Good}\\\\\n  \\text{Event-Based}      &amp; \\times       &amp; \\text{&#9888;&#65038;}               &amp; \\text{Fair}     &amp; \\text{&#9888;&#65038;}               &amp; \\text{Fair}\\\\\n\\end{array}&quot;,&quot;id&quot;:&quot;CXHNRWFXZG&quot;}" data-component-name="LatexBlockToDOM"></div><p>This table summarizes why there are really only two approaches worth considering. An &#10761; is a deal-breaker basically.</p><p>I ended up going with Per-Value Getters but I&#8217;ll give a brief overview of each approach with pros and cons below.</p><h3>Proxy</h3><p>Uses a JavaScript <code>Proxy</code> to create a theme object that dynamically resolves values when accessed. The proxy intercepts property lookups and returns either the light or dark value based on the current system theme.</p><blockquote><p><strong>What Is A </strong><code>Proxy</code><strong><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy"> Object</a>?</strong><br>Think of it like a wrapper object that can handle reactive side-effects. Example: <code>SessionData</code> with <code>Proxy</code>, where the <code>Proxy</code> syncs to the session cookie whenever a property of the <code>SessionData</code> object is updated.</p></blockquote><p><strong>Pros</strong>:<br>&#9989; Single event listener for entire theme<br>&#9989; Optimal memory efficiency<br>&#9989; No per-access computation</p><p><strong>Cons</strong>:<br>&#9888;&#65039; Requires wrapper function (<code>createTheme</code>)<br>&#9888;&#65039; Proxy behavior can be confusing to debug<br>&#9888;&#65039; Less granular garbage collection</p><h3>Per-Value Getters</h3><p>Each value created by <code>lightDark()</code> checks the current state of the dark mode media query.</p><p>We must be cautious with this approach because repeatedly checking the media query on every property access might be inefficient if done very frequently.</p><p>In practice, the media query check is very fast.</p><p><strong>Pros</strong>:<br>&#9989; Plain object syntax (no wrappers)<br>&#9989; Direct access to light/dark values<br>&#9989; Framework-agnostic implementation</p><p><strong>Cons</strong>:<br>&#9888;&#65039; Per-access computation cost<br>&#9888;&#65039; Complex memory management</p><h3>Value Objects</h3><p>Explicit object-oriented approach where <code>lightDark()</code> values are object literals used by a <code>Proxy</code> to toggle between modes.</p><p><strong>Pros</strong>:<br>&#9989; Optimal control and performance<br>&#9989; No hidden listeners<br>&#9989; Easy debugging</p><p><strong>Cons</strong>:<br>&#10060; No automatic theme updates<br>&#10060; Doesn't match CSS behavior<br>&#9888;&#65039; Verbose usage patterns<br>&#9888;&#65039; Manual subscription management</p><p>In a simple application this could be okay, but for me this was a no-go right off the bat since I was going for 1-1 CSS behavior and return values must be string-like.</p><h3>Event-Based</h3><p>Traditional observer pattern with manual subscription and update propagation. Maintains a centralized store that notifies subscribers when theme changes.</p><p><strong>Pros</strong>:<br>&#9989; Explicit control flow<br>&#9989; Framework-agnostic core<br>&#9989; Predictable updates</p><p><strong>Cons</strong>:<br>&#10060; Manual subscription management<br>&#10060; No string-like behavior<br>&#9888;&#65039; Memory leak potential<br>&#9888;&#65039; Update propagation complexity</p><p>This approach <em>might</em> be okay in an application already using similar patterns, like Redux or Signals. But I wanted to do this with as few peer dependencies as possible, so this approach was out.</p><h2>Why Not Use A Proxy Object?</h2><p>When I first started thinking about my solution, I initially wanted to wrap the theme in a <code>Proxy</code> object to check the dark-mode query anytime we accessed a property.</p><p>The main blocker here was that the <code>Proxy</code> knows the <code>theme</code>&#8217;s keys and values but doesn&#8217;t know how those values were instantiated.</p><p>Not every <code>theme</code> token uses <code>lightDark()</code>, and we would need a way to flag token values under the hood to know which ones do and which ones don&#8217;t.</p><p>This is where the &#8220;string-like&#8221; requirement comes from. Early on, I was thinking about using a custom symbol that the <code>Proxy</code> could look for to determine if it should check the dark-mode query.</p><p>As I played around with it, I realized that this approach was over-complicating things a bit. Instead of creating custom symbols, I realized I could streamline things with existing symbols like <code>Symbol.toPrimitive</code>.</p><p><code>Proxy</code> objects themselves can cause problems with reflection and typing because they&#8217;re not the actual objects we want, so I&#8217;m glad I was able to find a workaround.</p><h2>Without Further Ado</h2><p>The final implementation leverages JavaScript&#8217;s new(-ish) <code>FinalizationRegistry</code> (ES2021) for cleanup as well as clever primitive wrapping to create string-like theme values that auto-update when users change their system preferences.</p><h3>The Code</h3><pre><code>'use client';

let query: MediaQueryList | undefined;
let isDark = false;
let refs = 0;

const handleThemeChange = ({ matches }: MediaQueryListEvent) =&gt; {
  isDark = matches;
};

const colors = new FinalizationRegistry(() =&gt; {
  if (--refs &lt;= 0 &amp;&amp; query) {
    query.removeEventListener('change', handleThemeChange);
    query = undefined;
  }
});

export const lightDark = (light: string, dark: string) =&gt; {
  if (!query) {
    query = globalThis.matchMedia('(prefers-color-scheme: dark)');
    isDark = darkModeQuery?.matches || false;

    query?.addEventListener('change', handleThemeChange);
  }

  if (query) {
    const color = Object.assign('', {
      valueOf: () =&gt; isDark ? dark : light,
      toString: () =&gt; color.valueOf(),
      [Symbol.toPrimitive]: (hint: string) =&gt;
        hint === 'number'
          ? Number(color.valueOf())
          : color.valueOf(),
    });

    colors.register(color, null);
    refs++;
    
    return color;
  }

  return light;
};</code></pre><h3>The Demo</h3><p><a href="https://tsplay.dev/mAX7RN">Try it out here</a></p><div class="native-video-embed" data-component-name="VideoPlaceholder" data-attrs="{&quot;mediaUploadId&quot;:&quot;89144403-f656-4628-aab5-14f2c3d72c79&quot;,&quot;duration&quot;:null}"></div><h2>How It Works</h2><ul><li><p>The first <code>lightDark()</code> call initializes a single dark mode media query listener, avoiding duplicate listeners across multiple calls.</p></li><li><p><code>Object.assign('', {...})</code> fulfills our &#8220;string-like&#8221; requirement with custom versions of built-in string methods:</p><ul><li><p><code>valueOf()</code> checks the current query state and returns the right value</p></li><li><p><code>toString()</code> ensures string coercion in template strings</p></li><li><p><code>[Symbol.toPrimitive]</code> adds even better coercion for all primitives</p></li></ul></li><li><p><code>FinalizationRegistry</code> tracks when colors are garbage collected. When the last color is cleaned up, it removes the listener for the dark mode query to prevent memory leaks.</p></li><li><p>The <code>refs</code> counter ensures the media listener remains active while any theme object exists. When <code>refs</code> hits zero, resources clean up automatically.</p></li><li><p>If the function is called server-side, only the light-mode color is returned since the dark mode query will be <code>undefined</code>.</p></li></ul><h3>Updating The Theme</h3><ol><li><p>User changes system theme &#8594; browser fires a <code>change</code> event on the query</p></li><li><p><code>handleThemeChange()</code> updates <code>isDark</code> state accordingly</p></li><li><p>Subsequent <code>valueOf()</code> calls return new theme value, so all theme tokens automatically reflect the new state</p></li></ol><h2>Wrapping Up</h2><p>I&#8217;m honestly pretty amazed at how I was able to do this.</p><p>While this solution uses a more modern memory management API, I have a feeling it could be tweaked to use WeakMaps or WeakSets for better compatibility if needed.</p><p>The result is theme values that simply <em>work</em>, adapting to user preferences while maintaining perfect interoperability with existing styling paradigms.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><h2>Up Next</h2><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;ff14bf83-696b-432d-9e9d-cdda96e7c161&quot;,&quot;caption&quot;:&quot;Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Architecting Design Systems for Human Perception&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I&#8217;m a Staff Design Engineer who builds sexy, scalable enterprise-strength design systems.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-25T22:53:00.000Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dec40623-1506-4735-bb21-e324828114e3_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/design-systems-for-human-perception&quot;,&quot;section_name&quot;:&quot;Deeps&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:168566184,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;27207310-0306-4535-8ccf-07ce4c12a597&quot;,&quot;caption&quot;:&quot;Feature teams move slower than they should. What takes a week could take a day. Simple changes somehow break unrelated things. New hires need months to become useful.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[WIP] Building a Pit of Success with Inductive Reasoning&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I&#8217;m a Staff Design Engineer who builds sexy, scalable enterprise-strength design systems.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-24T22:01:39.994Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!v3zD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/building-a-pit-of-success&quot;,&quot;section_name&quot;:&quot;Deeps&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:166478385,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;a1f839c1-9ac9-4a01-b5c1-d9c43bf43918&quot;,&quot;caption&quot;:&quot;Icons are everywhere, yet they&#8217;re probably the easiest thing in your design system to screw up. I&#8217;ve seen so many <Icon> components that overcomplicate the DOM while doing nothing to make accessibility easier to achieve.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[WIP] Accessible SVG Icons The Right Way&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I build Design Systems and Micro-frontends in TypeScript and React with a focus on developer experience / Staff Frontend Engineer / TED curator / Ex-Apple (HIG) / 1.2M+ subs&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-24T22:17:59.979Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c54b3810-d9fa-4986-9775-77008fb03c90_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/accessible-svg-icons&quot;,&quot;section_name&quot;:&quot;Resources&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:166355744,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Any thoughts on what else you&#8217;d like to see? Leave a comment or hop into chat and let me know!</p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/how-i-built-csss-light-dark-in-typescript?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/how-i-built-csss-light-dark-in-typescript?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>If you enjoyed this post, please subscribe, hit the &#10084;&#65039; button, and share/restack &#128257; it with others who might find it helpful!</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The Cost of Outrage: How Hot Takes Fuel The Enshitification Of Software Engineering]]></title><description><![CDATA[Spreading Technical Disinformation Has Never Been Easier]]></description><link>https://blog.robhameetman.com/p/the-cost-of-outrage-how-hot-takes</link><guid isPermaLink="false">https://blog.robhameetman.com/p/the-cost-of-outrage-how-hot-takes</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Mon, 02 Jun 2025 11:02:36 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9e776b17-3ea6-4e22-aa83-df144ad5a4e7_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fKKV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fKKV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!fKKV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!fKKV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!fKKV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fKKV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1991758,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/164096986?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fKKV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!fKKV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!fKKV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!fKKV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F11fd352e-7ff5-41da-8164-ff1647b55f3c_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This morning, LinkedIn suggested this post in my feed:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1GbP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1GbP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 424w, https://substackcdn.com/image/fetch/$s_!1GbP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 848w, https://substackcdn.com/image/fetch/$s_!1GbP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 1272w, https://substackcdn.com/image/fetch/$s_!1GbP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1GbP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png" width="341" height="531.34" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1714,&quot;width&quot;:1100,&quot;resizeWidth&quot;:341,&quot;bytes&quot;:570752,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/164096986?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1GbP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 424w, https://substackcdn.com/image/fetch/$s_!1GbP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 848w, https://substackcdn.com/image/fetch/$s_!1GbP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 1272w, https://substackcdn.com/image/fetch/$s_!1GbP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f004035-1cd7-4d8b-a31f-48a4e1deb056_1100x1714.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p>I will never, ever use reduce in Javascript.<br>.<br>.<br>&#8221;Reduce is more elegant.&#8221;<br>&#8221;Real devs use functional methods&#8221;<br>&#8221;Loops are old-school.&#8221;<br>&#8221;It&#8217;s just one line, bro.&#8221;<br><br>These are not principles.<br>They&#8217;re just disguised best practices.</p><p>But here&#8217;s the truth:<br>You&#8217;re not writing code for applause.<br>You&#8217;re writing it to be understood.</p><p>&#128078; .reduce() hides logic in syntax<br>&#128078; It often obscures the intent.<br>&#128078; It sacrifices readability for cleverness</p><p>You know what doesn&#8217;t do that?<br>&#9989; A simple for loop.</p><p>Because when the code breaks at 2AM,<br>It won&#8217;t be the code that saves you.</p><p>It&#8217;ll be the line you can actually read quickly.</p></blockquote><p>The first line makes it clear right off the bat that this is rage bait. And particularly low-effort AI rage bait at that. It&#8217;s an opinion designed to be so outrageous and <s>unconventional</s> absurd that more seasoned engineers can&#8217;t help but comment, which in turn maximizes the author&#8217;s reach.</p><p>Content like this goes beyond basic ignorance or lack of experience/expertise into a level of technical disinformation that we as an industry need to get better at identifying and discouraging.</p><p>The key piece of context that pushes this into disinformation territory comes with a deeper understanding of the declarative paradigm itself, which focuses on making code more readable by building up small, compositional abstractions that can result in an almost DSL<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>-like experience at scale.</p><h3>A Quick Primer On Declarative Programming</h3><p>Most examples of declarative programming in JS/TS focus on built-in array methods, so if you look it up or ask AI you&#8217;re likely to get an example like this one, where let&#8217;s say a new array is created from an existing array by removing <code>null</code> values and converting the remaining numeric values to strings:</p><pre><code>const arr = [1, 2, 3, 4, null, 6, 7, null, 9, null];
const newArr = [] as Array&lt;string&gt;;

/* Imperative Approach */
for (let i = 0; i &lt; arr.length; i++) {
  if (arr[i] !== null) {
    newArr.push(String(arr[i]));
  }
}

/* Declarative Approach */
const newArr = arr.filter((value) =&gt; value !== null).map(String);</code></pre><p>While built-in <code>Array</code> methods like <code>.filter()</code>, <code>.map()</code>, and <code>.reduce()</code> are declarative, examples like these don&#8217;t fully illustrate why declarative code is advantageous beyond just eliminating a few lines of code here and there.</p><p>The paradigm itself is more about increasing readability by writing code in a way where the thing you want to do happens by simply calling a method <code>.doTheThing()</code> rather than describing <em>how</em> the thing is done. Declarative methods can use loops and other imperative control flows under the hood, so a slightly better example would be rewriting an implementation that starts out like this:</p><blockquote><p>&#128466;&#65039; <strong>Note<br></strong>You probably won&#8217;t actually have this kind of business logic in a real app, but it gets the idea across so hear me out.</p></blockquote><pre><code>interface Person {
  readonly name: string;
  position: energy;
  position: number;
}

const alice: Person = {
  name: 'Alice',
  energy: 100,
  position: 0,
};

const move = (
  person: Person,
  speed: number,
  distance = 1,
  effort = speed,
) =&gt; {
  if (person.energy) {
    for (let i = distance; i &gt; 0; i -= speed) {
      person.position += speed;
      person.energy -= effort;
    }
  }
};

const walk = (person: Person, distance = 1) =&gt;
  move(person, 0.5, distance);

const run = (person: Person, distance = 1) =&gt;
  move(person, 2, distance);

const jump = (person: Person, distance = 1) =&gt;
  move(person, 1, distance, 1.5);

walk(alice, 10);
run(alice, 5);
jump(alice, 4);
walk(alice, 8);</code></pre><p>While we do have our logic broken up into specialized functions, this code is not declarative because each function has to be told which <code>Person</code> is moving as a parameter, so we have to pass <code>alice</code> in as an argument every time. In a more functional approach, you could curry the <code>move()</code> function to get around this like so:</p><pre><code>const move = (person: Person) =&gt;
  (speed: number, distance = 1, effort = speed) =&gt; {
    if (person.energy) {
      for (let i = distance; i &gt; 0; i -= speed) {
        person.position += speed;
        person.energy -= effort;
      }
    }
  };

const aliceMoves = move(alice);
const aliceWalks = (distance = 1) =&gt; aliceMoves(0.5, distance);
const aliceRuns = (distance = 1) =&gt; aliceMoves(2, distance);
const aliceJumps = (distance = 1) =&gt; aliceMoves(1, distance, 1.5);

aliceWalks(10);
aliceRuns(5);
aliceJumps(4);
aliceWalks(8);</code></pre><p>This is a lot better in terms of readability but not so much scalability because we still need discrete functions for each person we want to move. In order to make this code declarative, we need to go beyond simply updating Alice&#8217;s position and return an updated copy of <code>alice</code>:</p><pre><code>interface Person {
  position: number;
  readonly name: string;
  readonly move(speed: number, distance: number, effort: number) =&gt; this;
  readonly walk(distance: number) =&gt; this;
  readonly run(distance: number) =&gt; this;
  readonly jump(distance: number) =&gt; this;
}

const alice: Person = {
  name: 'Alice',
  energy: 100,
  position: 0,
  move(speed: number, distance = 1, effort = speed) {
    if (this.energy) {
      for (let i = distance; i &gt; 0; i -= speed) {
        this.position += speed;
        this.energy -= effort;
      }
    }

    return this;
  },
  walk(distance = 1) {
    return this.move(0.5, distance);
  },
  run(distance = 1) {
    return this.move(2, distance);
  },
  jump(distance = 1) {
    return this.move(1, distance, 1.5);
  },
};</code></pre><p>Writing our code this way allows us to move Alice like this:</p><pre><code>alice.walk(10).run(5).jump(4).walk(8);</code></pre><p>Like I said, you probably won&#8217;t actually shift a <code>Person</code> object&#8217;s position in a real app, but the same principles apply whenever you need a series of heterogeneous transformations (e.g., applying user profile updates, composing query filters, or processing form data.)</p><blockquote><p>&#9757;&#65039; <strong>If this section was helpful let me know!</strong><br>I&#8217;m considering writing up a more in-depth post that dives into this with more relevant examples; I might also do it as a 2-part series that builds on this and shows how I&#8217;ve used RxJS with React in the past to build web apps with declarative streams that have insane performance.</p></blockquote><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>Like what you&#8217;re seeing so far?</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h3>Technical Disinformation</h3><p>Now that we understand what declarative methods like <code>.reduce()</code> are all about, we can start to get a better sense of why this kind of content isn&#8217;t eligible for the same benefits of the doubt we would typically afford the author of the rage bait from earlier, who has just under 3 years of experience at this point.</p><p>Given the examples above, we can see how the &#128078; points being labelled as such is misleading. Except for the last one, the points themselves are fine but should be &#128077; instead. Hiding logic in syntax is a feature of declarative code not a bug. It is, in fact, the point.</p><p>The only time I&#8217;ve seen <code>.reduce()</code> kinda sacrifice readability for cleverness is when it&#8217;s used to pipe async functions sequentially, like this:</p><pre><code>asyncFunctions.reduce(
  (resolved, next) =&gt;
    resolved
      .then((result) =&gt; next(result))
      .catch(console.error),
  Promise.resolve(),
);</code></pre><p>In most cases though, it&#8217;s used to build objects from arrays in situations where <code>Object.fromEntries()</code> isn&#8217;t an option.</p><p>The final two sentences would normally get a pass as well (albeit with a raised eyebrow) but given how sus his whole post is already, I&#8217;m not inclined to start now. Anyone who&#8217;s actually had to respond to a production incident at 2am can tell you it&#8217;s not simple code that saves you, it&#8217;s your recognition- which is faster than recall- leveraging the mental shortcuts you&#8217;ve built up.</p><h3>Newbz These Days!</h3><p>In the beginning (of people teaching themselves to code,) we had books. Fat. Ass. Books. My dad had a book somewhere on one of his bookshelves on C++ that&#8217;s 30 years old now and girthier than my whole fist. Expert software engineers were the people who literally wrote the canonical book or manual on the technical subject of their expertise, or they made a name for themselves as the creator of that subject. Plenty did both.</p><p>Even in the 2010s, posting something like this on your Medium blog <em>intentionally</em> as an aspiring professional would be akin to shouting &#8220;I&#8217;m just an imposter, don&#8217;t hire me!&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M5CH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M5CH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 424w, https://substackcdn.com/image/fetch/$s_!M5CH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 848w, https://substackcdn.com/image/fetch/$s_!M5CH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 1272w, https://substackcdn.com/image/fetch/$s_!M5CH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!M5CH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp" width="292" height="349.63157894736844" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0394df66-3860-450f-8744-81b0954ee572_760x910.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:910,&quot;width&quot;:760,&quot;resizeWidth&quot;:292,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!M5CH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 424w, https://substackcdn.com/image/fetch/$s_!M5CH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 848w, https://substackcdn.com/image/fetch/$s_!M5CH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 1272w, https://substackcdn.com/image/fetch/$s_!M5CH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0394df66-3860-450f-8744-81b0954ee572_760x910.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The tradition of sharing knowledge in this way meant that people positioning themselves as rising stars still had to demonstrate a solid understanding of more sophisticated technique, or at least an eagerness to learn.</p><p>Aimee Knight and Lydia Hallie come to mind as engineers who stood out to me around this time as particularly admirable for their ability to bring others along with them as they entered the field and grew. As Juniors they had no problem talking shop with seasoned experts. If you take a look at <a href="https://www.aimeemarieknight.com/">Aimee's blog</a> today, it&#8217;s as technical as one would expect from an expert software engineer. Lydia&#8217;s technical expertise and her desire to share it continues to speak for itself.</p><h3>Juniors Cannot Be Junior Anymore</h3><p>Bootcamps have flooded the industry with new Engineers, especially on the frontend. While the idea that this number doubles every 5 years is more of a myth these days, <a href="https://survey.stackoverflow.co/2024/developer-profile#3-years-of-professional-coding-experience-by-developer-type">data from the most recent Stack Overflow Annual Developer Survey</a> does show that the average Frontend Engineer has less than 8 years of experience.</p><p>I&#8217;ve definitely noticed the impact of this especially over the past few years; at least the last two organizations I've been at have had ICs under Staff that were a standard deviation below what I&#8217;d typically expect in terms of technical expertise <em>at every level</em>. And it&#8217;s not just me who&#8217;s noticed! In one of these roles this was a widely acknowledged fact amongst the Staff+ group.</p><p>At first I chalked it up to that organization having scaled too rapidly, but seeing this pattern across multiple organizations consecutively has made the talent gap in the industry crystal clear.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eieD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eieD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 424w, https://substackcdn.com/image/fetch/$s_!eieD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 848w, https://substackcdn.com/image/fetch/$s_!eieD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 1272w, https://substackcdn.com/image/fetch/$s_!eieD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eieD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png" width="498" height="383.0769230769231" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9f77d02-446c-4143-8afb-205c945d1db6_650x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:650,&quot;resizeWidth&quot;:498,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!eieD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 424w, https://substackcdn.com/image/fetch/$s_!eieD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 848w, https://substackcdn.com/image/fetch/$s_!eieD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 1272w, https://substackcdn.com/image/fetch/$s_!eieD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9f77d02-446c-4143-8afb-205c945d1db6_650x500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">&#128279; link: https://medium.com/@jankammerath/the-junior-developer-role-has-become-great-again-85e47b7bcbde</figcaption></figure></div><p>With the advent of AI upon us, Juniors by today&#8217;s standards have been all but replaced, along with some contractors who do team augmentation as an extra pair of hands. We don&#8217;t need new people who write code without having any idea how it works because we have AI for that now! And AI is at least much, much faster.</p><p>But companies aren&#8217;t just going to stop hiring Juniors. ICs get old after a while, and expensive, and become consultants or go into management or leadership or retire early. We still need new folks to start somewhere, so the job description needs a revamp. The problem for recent grads is that the job market, already oversaturated as it is, is insanely bottom-heavy.</p><p>The intensity of this competition effectively raises the bar and, I believe, will eventually close the talent gap. The demand for experts is decreasing<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>, but the value of expertise itself is actually increasing. What I would never tell a candidate I&#8217;m passing on is that you can have an Ivy League CS degree and 5 FAANG/MAMAA internships but there&#8217;s a line of folks out the door who are just as eager to learn and capable of doing so just as quickly, so if you&#8217;re only as technical as your AI then you aren&#8217;t going to yield the best return on investment.</p><blockquote><p>&#128173; <strong>BTW</strong><br>If you do actually have an Ivy League CS degree and 5 FAANG/MAMAA internships you&#8217;re gonna be fine. Eventually.</p></blockquote><h3>Wrapping This Up</h3><p>Back to the LinkedIn post at the top, it should go without saying at this point that dishing out a blatantly bad technical opinion is probably a bad idea. It&#8217;s not that it won&#8217;t work- it unfortunately does work to a certain degree or else this guy wouldn&#8217;t be doing it.</p><p>Not sure how many followers he added but his comments were riddled with Seniors calling him out as a bad engineer (&#8220;sounds like a skill issue&#8221;). And that was unfortunately the whole idea. So I threw him on my blocklist, and I&#8217;m sure I wasn&#8217;t the only one.</p><p>But if I&#8217;m looking at the trade-offs, in the long-run, building an audience at the cost of connecting with more technical peers who strive to make a name for themselves by actually knowing what they&#8217;re talking about doesn&#8217;t seem worth it. These folks will someday surpass you both in talent and pay, if they aren&#8217;t already ahead. And this will lock you in as Yet Another Engineer Who Only Writes About Soft Skills, because the audience that you do have are going to be chalk full of vibe coders<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> and Product/Growth Developers.</p><p>Look, I&#8217;m not even saying don&#8217;t ever use rage bait. We all do it sometimes; I have opinions in my Notes that, while genuine, definitely come across as rage bait-ey. What matters is that these are opinions I&#8217;ve formed as a result of a decade of industry experience; I&#8217;m not contriving them just to increase my numbers.</p><p>If you <em>must</em> resort to technical hot takes on LinkedIn, at least use <strong>The &#8220;Prove Me Wrong&#8221;</strong> variant so you don&#8217;t make yourself look like an asshole who is unable or unwilling to take feedback in code review.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><blockquote><p>&#128591; BTW my subscribers are <strong>the best</strong>, my previous post on <em><strong><a href="https://blog.robhameetman.com/p/aligning-components-with-custom-html?r=2ypy2w">Aligning Components with Custom HTML Attributes</a></strong></em> achieved an 80% open rate and I can&#8217;t thank each of you enough for taking the time to read it!</p></blockquote><h2>Up Next</h2><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;64b5c768-58cf-4d76-91bc-a4066f5f4653&quot;,&quot;caption&quot;:&quot;Feature teams move slower than they should. What takes a week could take a day. Simple changes somehow break unrelated things. New hires need months to become useful.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[WIP] Building a Pit of Success with Inductive Reasoning&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I&#8217;m a Staff Design Engineer who builds sexy, scalable enterprise-strength design systems.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-24T22:01:39.994Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!v3zD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F818fc660-922d-4795-992a-dfaeefa0d752_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/building-a-pit-of-success&quot;,&quot;section_name&quot;:&quot;Deeps&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:166478385,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2076716,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;2168e223-8f1b-4760-8fee-fc2ed39543b7&quot;,&quot;caption&quot;:&quot;Icons are everywhere, yet they&#8217;re probably the easiest thing in your design system to screw up. I&#8217;ve seen so many <Icon> components that overcomplicate the DOM while doing nothing to make accessibility easier to achieve.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[WIP] Accessible SVG Icons The Right Way&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I&#8217;m a Staff Design Engineer who builds sexy, scalable enterprise-strength design systems.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-24T22:17:59.979Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c54b3810-d9fa-4986-9775-77008fb03c90_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/accessible-svg-icons&quot;,&quot;section_name&quot;:&quot;Resources&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:166355744,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2076716,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;14412bb5-e6bd-4dc1-bc53-718fc3a028f5&quot;,&quot;caption&quot;:&quot;Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Architecting Design Systems for Human Perception&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I&#8217;m a Staff Design Engineer who builds sexy, scalable enterprise-strength design systems.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-08-25T22:53:00.000Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dec40623-1506-4735-bb21-e324828114e3_2464x1856.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/design-systems-for-human-perception&quot;,&quot;section_name&quot;:&quot;Deeps&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:168566184,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:4,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2076716,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Any thoughts on what else you&#8217;d like to see? Leave a comment or hop into chat and let me know! I&#8217;ll be on vacation most of this month but have plenty to dive into as soon as I&#8217;m back.</p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/the-cost-of-outrage-how-hot-takes?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/the-cost-of-outrage-how-hot-takes?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>If you enjoyed this post, please subscribe, hit the &#10084;&#65039; button, and share/restack &#128257; it with others who might find it helpful!</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Domain-Specific Language- a specialized language used for certain contexts, e.g. <a href="https://cucumber.io/docs/gherkin/reference">Gherkin</a> for BDD-style testing.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Demand for experts has been decreasing since entrepreneurs started shifting away from scalability toward acquirability.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>By &#8220;vibe coders&#8221; I mean people who don&#8217;t review or correct AI-written code, not people who use AI-integrated tooling in general.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Aligning Components with Custom HTML Attributes]]></title><description><![CDATA[Leveraging CSS Attribute Selectors in Your Design System For Easy Positioning]]></description><link>https://blog.robhameetman.com/p/aligning-components-with-custom-html</link><guid isPermaLink="false">https://blog.robhameetman.com/p/aligning-components-with-custom-html</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Tue, 06 May 2025 10:01:48 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/edcb3d0c-2d4f-4abb-b16a-ce04994327ab_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!itsP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!itsP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!itsP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!itsP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!itsP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!itsP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:954282,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/162935061?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!itsP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!itsP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!itsP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!itsP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bcf9247-dc2d-4f73-9fee-2738ba25c49d_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Over the weekend, I found myself updating my personal React template repo and wanted add a few components to the pattern library.</p><p>I love the look and feel of <a href="https://www.heroui.com/">HeroUI</a>, so I initially built the UI with it. Since then, it&#8217;s introduced breaking changes pretty much every time I&#8217;ve done a dependency update even in minor versions. I finally ripped it out a few weeks ago and have been referring to HeroUI source code and documentation occasionally as I rebuild my core components.</p><p>Keeping changes to props (i.e. the contract) minimal is crucial to ensuring the scope stays manageable, so I pulled up <a href="https://www.heroui.com/docs/components/badge">the HeroUI docs</a> as I started building out my new <code>&lt;Badge /&gt;</code> component on Saturday.</p><p>One thing I noticed right off the bat is that the HeroUI <code>&lt;Badge /&gt;</code> component wraps the <code>&lt;Avatar /&gt;</code> component and not the other way around:</p><pre><code>&lt;Badge color="primary" content="5" placement="bottom-right"&gt;
    &lt;Avatar radius="md" size="lg" src="..." /&gt;
&lt;/Badge&gt;</code></pre><p>This implementation makes sense but feels rudimentary. I wanted to know if I could achieve something that expresses the presentation logic as an avatar with a badge rather than a badge over an avatar, something like this instead:</p><pre><code>&lt;Avatar
  badge={&lt;Badge color="primary" placement="bottom-right"&gt;5&lt;/Badge&gt;}
  radius="md"
  size="lg"
  src="..."
/&gt;</code></pre><p>We might have a traditional badge, with a number or icon representing something of priority like notifications or a word like &#8220;PRO&#8221;. We might also use it to indicate online  status or selection within a group, so being able to change the badge position is actually a requirement!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MKPz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MKPz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 424w, https://substackcdn.com/image/fetch/$s_!MKPz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 848w, https://substackcdn.com/image/fetch/$s_!MKPz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 1272w, https://substackcdn.com/image/fetch/$s_!MKPz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MKPz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png" width="724" height="430" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:430,&quot;width&quot;:724,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:193263,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/162935061?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MKPz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 424w, https://substackcdn.com/image/fetch/$s_!MKPz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 848w, https://substackcdn.com/image/fetch/$s_!MKPz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 1272w, https://substackcdn.com/image/fetch/$s_!MKPz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd9188cc3-7d3f-4780-9d39-6e3c009324fb_724x430.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It just so happens that I had a standardized implementation for an <code>alignment</code> prop lying around that could work in this case in theory so I decided to break the existing <code>BadgeProps</code> contract.</p><p>This approach is nothing new or novel&#8212; any Engineer who uses CSS somewhat regularly or at least has deeper than a surface-level understanding of it could ideate a solution like this on their own, but I figure it&#8217;s worth sharing in the hopes that someone finds it helpful!</p><h2><code>Alignment</code> Tokens</h2><p>As tokens, <code>alignment</code> prop values describe a position relative to one or two perpendicular sides of a box, in this case the <a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Box_model">Box Model</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EWaI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EWaI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 424w, https://substackcdn.com/image/fetch/$s_!EWaI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 848w, https://substackcdn.com/image/fetch/$s_!EWaI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 1272w, https://substackcdn.com/image/fetch/$s_!EWaI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EWaI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png" width="1456" height="841" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:841,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1557380,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/162935061?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!EWaI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 424w, https://substackcdn.com/image/fetch/$s_!EWaI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 848w, https://substackcdn.com/image/fetch/$s_!EWaI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 1272w, https://substackcdn.com/image/fetch/$s_!EWaI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fb7fae3-2844-43e9-96fb-aa3ce555daa4_2428x1402.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The idea is to be able to implement a 9-anchor positioning system, not unlike one you might find in a design tool like Figma or Photoshop.</figcaption></figure></div><p>If you&#8217;re wondering why I&#8217;m calling it a &#8220;9-anchor&#8221; system when there are only 8 visible anchors in the screenshot, it&#8217;s because we want to use these for positioning rather than sizing so we can imagine the 9th anchor being directly in the center.</p><p>Since we&#8217;re operating on a 2-dimensional plane, I broke these tokens down into two enums- one for each axis:</p><pre><code>export enum HorizontalAlignment {
    Left = 'left',
    Center = 'center',
    Right = 'right',
}

export enum VerticalAlignment {
    Bottom = 'bottom',
    Center = 'center',
    Top = 'top',
}</code></pre><p>If you&#8217;re a technical design nerd like I am, you may have the urge to yell at your screen that <code>VerticalAlignment.Center</code> should be <code>VerticalAlignment.Middle</code>! And you wouldn&#8217;t necessarily be wrong, but in this implementation I&#8217;ve found it&#8217;s actually better <strong>not</strong> to distinguish between horizontal and vertical center. Keep reading and you&#8217;ll see why!</p><p>Anyhow, these two enums are fed into token types like so for prop signatures:</p><pre><code>export type ScalarAlignment = `${VerticalAlignment}` | `${HorizontalAlignment}`;

export type CompoundAlignment = `${VerticalAlignment}${'-' | ' '}${HorizontalAlignment}`;

export type Alignment = ScalarAlignment | CompoundAlignment;</code></pre><p>Notice how we&#8217;re using template string literal types. Using a string enum in a template string literal type expands the enum type into a union of its values. This allows our Product Developer (so&#8230; future me) to use a discrete horizontal or vertical alignment value like &#8220;top&#8221;, &#8220;center&#8221;, &#8220;left&#8221;, etc. or a compound horizontal and vertical value separated by either a dash or space e.g. &#8220;top-center&#8221;, &#8220;bottom right&#8221;, etc.</p><p>Provided to a component, the value is simply passed directly to the root element as an HTML <code>data-* </code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/data-*">custom attribute</a>:</p><pre><code>&lt;span
    className={css}
    style={{ ...style, borderRadius: circle ? '50%' : radius }}
    <strong>data-alignment={alignment}</strong>
    ref={ref}
&gt;
    {children ? children : &lt;&gt;&amp;nbsp;&lt;/&gt;}
&lt;/span&gt;</code></pre><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>&#10084;&#65039; Like what you&#8217;re seeing so far?</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>Leveraging CSS Attribute Selectors</h2><p>Now that we have a custom attribute to select, we can control our badge position externally.</p><p>You can easily make this DRY in SCSS with a mixin or placeholder selector. For this example I&#8217;m using avatar&#8217;s stylesheet, but may end up moving most of the below to <code>src/App.css</code> since I&#8217;m using PostCSS Modules:</p><pre><code>// Avatar.module.css

.avatar {
    position: relative;

    [data-alignment] {
        position: absolute;
    }

    [data-alignment$="left"] {
        left: 0;
    }

    [data-alignment$="right"] {
        right: 0;
    }

    [data-alignment^="top"] {
        top: 0;
    }

    [data-alignment^="bottom"] {
        bottom: 0;
    }

    [data-alignment]:not([data-alignment^="top"]):not([data-alignment^="bottom"]):not([data-alignment$="left"]):not([data-alignment$="right"]):not([data-alignment*="center"]),
    [data-alignment$="top"],
    [data-alignment$="bottom"],
    [data-alignment$="center"] {
        left: 50%;
        transform: translateX(-50%);
    }

    [data-alignment]:not([data-alignment^="top"]):not([data-alignment^="bottom"]):not([data-alignment$="left"]):not([data-alignment$="right"]):not([data-alignment*="center"]),
    [data-alignment^="left"],
    [data-alignment^="right"],
    [data-alignment^="center"] {
        transform: translateY(-50%);
        top: 50%;
    }
}</code></pre><p>Let&#8217;s break this down:</p><pre><code>.avatar {
    position: relative;

    [data-alignment] {
        position: absolute;
    }</code></pre><p>This code helps us position the component relative to the underlying content. The second rule in particular makes it easier to align the component by giving us a mechanism that lets us start at 0 on a pixel coordinate grid from each side of the content.</p><p>It&#8217;s worth noting that because we&#8217;re using absolute positioning, <code>position: relative;</code><strong> </strong>in the containing component <strong>is required for this to work properly.</strong></p><pre><code>[data-alignment$="left"] {
    left: 0;
}

[data-alignment$="right"] {
    right: 0;
}</code></pre><p>These selectors target any alignment values ending in &#8220;left&#8221; or &#8220;right&#8221;. We know compound values always end with a horizontal alignment, so this covers &#8220;*-left&#8221;, &#8220;*-right&#8221;, &#8220;left&#8221;, and &#8220;right&#8221;.</p><pre><code>[data-alignment^="top"] {
    top: 0;
}

[data-alignment^="bottom"] {
    bottom: 0;
}</code></pre><p>These selectors do the same thing but for vertical alignment. Compound values always start with a vertical alignment, so we use <code>^=</code> instead of <code>$=</code> to match the beginning of the string rather than the end. So now we&#8217;ve also taken care of &#8220;top-*&#8221;, &#8220;bottom-*&#8221;, &#8220;top&#8221;, and &#8220;bottom&#8221;.</p><p>Here&#8217;s where it gets a little weird:</p><pre><code>[data-alignment]:not([data-alignment^="top"]):not([data-alignment^="bottom"]):not([data-alignment$="left"]):not([data-alignment$="right"]):not([data-alignment*="center"]),
[data-alignment$="top"],
[data-alignment$="bottom"],
[data-alignment$="center"] {
    left: 50%;
    transform: translateX(-50%);
}

[data-alignment]:not([data-alignment^="top"]):not([data-alignment^="bottom"]):not([data-alignment$="left"]):not([data-alignment$="right"]):not([data-alignment*="center"]),
[data-alignment^="left"],
[data-alignment^="right"],
[data-alignment^="center"] {
    transform: translateY(-50%);
    top: 50%;
}</code></pre><p>Being the astute reader you are, you probably noticed that the attribute selector operators have flipped, so now we&#8217;re using <code>^=</code> for horizontal alignment <code>$=</code> for vertical. We already know that a compound value always begins with a vertical alignment and ends with a horizontal alignment, so by swapping the operators we can isolate discrete values, and that&#8217;s what this code does.</p><p>As a requirement, whenever we use a discrete alignment value like &#8220;top&#8221; or &#8220;left&#8221;, it should be evaluated as a compound value <strong>where the missing axis is assumed to be &#8220;center&#8221;</strong>. So &#8220;top&#8221; should be evaluated as &#8220;top-center&#8221;, &#8220;left&#8221; should be evaluated as &#8220;center-left&#8221;&#8230; you get the idea!</p><p>This styling centers the alignable component both when &#8220;center&#8221; is explicit and implicit. And this right here is why we don&#8217;t distinguish between horizontal &#8220;center&#8221; and vertical &#8220;middle&#8221;! Plus, semantically it&#8217;s just much nicer to be able to say &#8220;center&#8221; rather than &#8220;middle-center&#8221;.</p><p>Now, for an avatar component, having a badge in the direct center feels like a potential use case I&#8217;d disregard as YAGNI. But who knows, I&#8217;ve seen stranger things out there.</p><pre><code>[data-alignment]:not([data-alignment^="top"]):not([data-alignment^="bottom"]):not([data-alignment$="left"]):not([data-alignment$="right"]):not([data-alignment*="center"])</code></pre><p>Lastly, I want to point out this line. It&#8217;s a part of both of these last two selectors and it plays in important role in graceful degradation by ensuring that any invalid value renders the component in the direct center, as if the value were &#8220;center-center&#8221; or just &#8220;center&#8221;.</p><p>I&#8217;ve found a few edge cases (like <code>alignment=&#8220;bright&#8221;</code>) that can break this but for the most part it&#8217;s pretty solid, I&#8217;d feel fine handing this off to QE at this point if I did this at Vivid Seats. That said, I haven&#8217;t even started with animations yet and I&#8217;ll be nervous about <code>transform: translate;</code> when I do. If I want to be able to animate the badge with a slide in/out, I may have to figure out another approach.</p><p>I&#8217;m guessing that&#8217;s why HeroUI&#8217;s <code>&lt;Badge&gt;</code> is the container.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/aligning-components-with-custom-html?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/aligning-components-with-custom-html?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>If you enjoyed this post, please subscribe, hit the &#10084;&#65039; button, and share/restack &#128257; it with others who might find it helpful!</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Vibe Coding]]></title><description><![CDATA[How do we feel about it? Is it the art of Software Engineering a relic of the past? Or the key to AI-driven innovation?]]></description><link>https://blog.robhameetman.com/p/vibe-coding</link><guid isPermaLink="false">https://blog.robhameetman.com/p/vibe-coding</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Thu, 06 Mar 2025 06:36:54 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1eb88199-7194-4b4d-97cd-37d85d87e60a_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FiC3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FiC3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!FiC3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!FiC3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!FiC3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FiC3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2171404,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/158493896?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FiC3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!FiC3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!FiC3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!FiC3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77b92a88-9d9a-421a-b344-d8eb02ff8887_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Let's be real. What we're calling <strong>vibe coding</strong> today&#8212;people throwing vague prompts at AI and expecting magic&#8212;isn't just another step in programming's evolution. It's a fundamental rupture in how we create software.</p><p>I've been watching Engineers react to this shift with skeptical fascination. The fascination comes from seeing people with zero technical knowledge suddenly building dope shit by describing what they want in plain English. The skepticism? That it mostly actually works&#8230; at least until it doesn&#8217;t.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>&#10084;&#65039; Like what you&#8217;re seeing so far?</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>What's Actually Happening Here</h2><p>The term "vibe coding" captures something real: the collapse of traditional knowledge barriers in software creation. But underneath the memes and the moral panic, something more interesting is happening.</p><p>Programming has always been about layers of abstraction. We started with machine code, moved to assembly, jumped to higher-level languages, then frameworks, and now we're approaching a layer where natural language itself becomes the interface to computation.</p><p>This one doesn't just hide implementation details. It hides the very concept of implementation.</p><h2>Not Just Another Tool in the Box</h2><p>Whenever new technology emerges, there's this predictable chorus of "it's just another tool" or "<em>real engineers</em> will still be valuable." That's comforting but misses the point.</p><p>AI isn't just another abstraction layer like high-level languages were to assembly. Previous abstractions required you to understand programming concepts. They just made them more convenient. You still needed to think algorithmically.</p><p>This is different. A great model doesn't just abstract away implementation. It abstracts away the need to think like a programmer at all.</p><p>That's not an incremental change. That's a phase transition.</p><h2>Language Creates New Possibilities</h2><p>What's also fascinating is how the term "vibe coding" itself is shaping reality. By naming this practice, by giving legitimacy to what would previously have been dismissed as "not real programming," we're creating space for entirely new approaches to software creation.</p><p>The term validates non-traditional paths into tech. It challenges the gatekeeping that's been baked into software culture. It suggests that technical intuition might be as valuable as technical knowledge.</p><p>And, I&#8217;ll admit, that <em>can be</em> frustrating as hell to someone who spent years mastering the craft.</p><h2>Where This Actually Leads</h2><p>Most discussions about AI and coding fall into simplistic traps: either "AI will replace programmers" or "AI is just a tool that makes programmers more productive."</p><p>The reality is messier and more interesting.</p><p>What we're witnessing isn't the death of programming. It's a bifurcation. Two distinct disciplines are emerging from what was once a single field:</p><ol><li><p><strong>System Engineering</strong><br>Designing, architecting, and maintaining the complex systems that underpin our digital world. This requires deep technical knowledge and won't be replaced by vibes anytime soon.</p></li><li><p><strong>Product Engineering</strong><br>Creating specific solutions to business problems using AI as the primary implementation layer. This is where vibe coding thrives, and it's becoming its own discipline with its own expertise.</p></li></ol><p>These aren't the same field anymore. They require different skills, different mindsets, and different types of people.</p><h2>The New Knowledge Hierarchy</h2><p>Here's where things get really interesting. Vibe coding doesn't eliminate the need for expertise. It creates demand for new types of expertise:</p><ul><li><p><strong>Prompt Engineering</strong><br>Not just writing good prompts, but understanding the conceptual boundaries of what current AI can and cannot accomplish.</p></li><li><p><strong>Result Verification</strong><br>Developing reliable methods to verify that AI-generated code actually does what it's supposed to do, without necessarily understanding all implementation details.</p></li><li><p><strong>System Design</strong><br>Creating architectures that work reliably even when individual components are generated by AI with varying levels of reliability.</p></li></ul><p>None of these are simple skills. They're just different from traditional programming skills, and they'll be valuable in different contexts.</p><h2>What Actually Matters Now</h2><p>The most valuable people in this new landscape won't be those who cling to traditional programming or those who blindly embrace vibe coding. They'll be those who can bridge these worlds. People who understand enough about implementation to know what's possible, but who can translate between human intent and computational reality.</p><p>If you're panicking about AI replacing Software Engineers, you're headed down the wrong path. The real question is: <strong>how does your role evolve when implementation is no longer the bottleneck?</strong></p><p>Some possibilities:</p><ul><li><p>Becoming the person who can frame problems in ways that make them easily solvable</p></li><li><p>Developing expertise in verifying and validating solutions</p></li><li><p>Building the frameworks and platforms that make AI-assisted development reliable</p></li><li><p>Creating the tools that let others build with confidence</p></li></ul><h2>Learning From Historical Parallels</h2><p>The closest parallel to what's happening now isn't any previous programming paradigm shift. It's what happened to other fields when they were transformed by computation.</p><p>Think about graphic design before and after desktop publishing. Or architecture before and after CAD. Or animation before and after computer graphics.</p><p>In each case, the field didn't disappear. It transformed. Some traditional skills became less valuable. New skills emerged. The nature of expertise shifted.</p><p>The people who thrived weren't those who resisted change or those who abandoned all traditional knowledge. They were those who understood which fundamentals remained valuable and which needed to evolve.</p><h2>The Only Path Forward</h2><p>The future belongs to those who can navigate the middle ground. People who can bring enough technical knowledge to avoid AI's pitfalls, but enough flexibility to leverage its capabilities.</p><p>AI isn't just another tool. Nor is it the end of programming either. It's the beginning of something different. Something that will eventually just be called "coding" again, but will mean something new.</p><p>The question isn't whether you embrace AI or resist it. The question is: what aspects of software creation still require human expertise, and how do you position yourself at that intersection?</p><p>That's where the opportunities are. That's where the future is being built.</p><p>And it's going to be a wild ride!</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/vibe-coding?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/vibe-coding?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>If you enjoyed this post, please subscribe, hit the &#10084;&#65039; button, and share/restack &#128257; it with others who might find it helpful</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The Newest Version of Claude Created a UI Pattern I’ve Never Seen Before]]></title><description><![CDATA[What happens when AI challenges conventional design system patterns?]]></description><link>https://blog.robhameetman.com/p/the-newest-version-of-claude-created-a-ui-pattern-ive-never-seen-before</link><guid isPermaLink="false">https://blog.robhameetman.com/p/the-newest-version-of-claude-created-a-ui-pattern-ive-never-seen-before</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Tue, 25 Feb 2025 11:15:25 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1dd244ee-7afc-452e-825f-7d31e2c97e35_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Kur5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Kur5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!Kur5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!Kur5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!Kur5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Kur5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1940612,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/157864768?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Kur5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!Kur5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!Kur5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!Kur5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbeb0ce2-1df7-4db3-b746-909b9a19fd3d_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Over the past month, I've been exploring how AI can accelerate the code mockup process for design system components. We started with a structured methodology, then moved into comparing leading AI models like <strong>ChatGPT o3, Claude 3.5 Sonnet, and DeepSeek R1.</strong></p><p>Today, Anthropic released Claude Code and <strong>Claude 3.7 Sonnet</strong>, a new model with an extended reasoning mode to mirror the complex chain-of-thought that precedes output by <strong>ChatGPT o3-mini-high</strong> and <strong>DeepSeek R1</strong>.</p><p>DeepSeek shattered my expectations by demonstrating that AI doesn&#8217;t necessarily always get worse as complexity increases, though Claude 3.5 Sonnet proved an impressively close runner-up. Can Claude 3.7 Sonnet&#8217;s reasoning one-up DeepSeek&#8217;s?</p><p>Let&#8217;s find out!</p><div><hr></div><h2>Testing The Same Components</h2><p>To maintain consistency with my previous tests, I evaluated <strong>Claude 3.7 Sonnet</strong> against the same three components:</p><ul><li><p><strong>&lt;Kbd /&gt;</strong> - Our atomic component representing keyboard keys</p></li><li><p><strong>&lt;Breadcrumbs /&gt;</strong> - Our molecular component for navigational breadcrumbs</p></li><li><p><strong>&lt;Table /&gt;</strong> - Our complex organism component for data display</p></li></ul><p>Let's dive in and see how Claude 3.7 Sonnet performed in each case.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#10084;&#65039; Like what you&#8217;re seeing so far?</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p><code>&lt;Kbd /&gt;</code><br>&#9989; <strong>Passable Rough Draft</strong></p><ul><li><p><strong>Pattern: </strong>Standalone Component</p></li><li><p><strong>Approach #1: Individual Key Components</strong></p></li></ul><pre><code>// Single key
&lt;Kbd&gt;A&lt;/Kbd&gt;

// Key combination (manual arrangement)
&lt;span&gt;
  &lt;Kbd&gt;Ctrl&lt;/Kbd&gt;
  &lt;span&gt;+&lt;/span&gt;
  &lt;Kbd&gt;C&lt;/Kbd&gt;
&lt;/span&gt;

// With styling variants
&lt;Kbd size="small" variant="outline"&gt;Tab&lt;/Kbd&gt;

// Using special key constants
&lt;Kbd&gt;{KEY_SYMBOLS.COMMAND}&lt;/Kbd&gt;</code></pre><ul><li><p><strong>Approach #2: Combination Support Built-in</strong></p></li></ul><pre><code>// Single key
&lt;Kbd&gt;A&lt;/Kbd&gt;

// Key combination as array
&lt;Kbd keys={["Ctrl", "C"]} /&gt;

// Custom separator
&lt;Kbd keys={["Cmd", "Option", "Esc"]} separator="+" /&gt;

// With styling variants
&lt;Kbd keys={["Shift", "Tab"]} size="small" variant="outline" /&gt;

// Using key binding object
&lt;Kbd keyBinding={{ 
  modifier: ["Ctrl", "Alt"],
  key: "Delete"
}} /&gt;</code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Kbd</strong>: <code>keys</code>, <code>keyBinding</code>, <code>separator</code>, <code>size</code>, <code>variant</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Functions:</strong> <code>normalizeKeyNames()</code>, <code>formatKeyBinding()</code></p></li><li><p><strong>Types: </strong><code>KeyBinding</code>, <code>KbdProps</code>, <code>KbdSize</code>, <code>KbdVariant</code></p></li><li><p><strong>Constants:</strong> <code>KBD_SIZE</code>, <code>KBD_VARIANT</code>,<code> KEY_SYMBOLS</code>, PLATFORM_KEYS</p></li></ul></li></ul><h4>Review</h4><p>The last test for this component was the first time Claude really <strong>"wow!"&#8217;d</strong> me. Unlike <strong>ChatGPT o3-mini-high</strong> and <strong>DeepSeek R1</strong>, which <strong>considered but rejected</strong> a compound component approach, Claude&#8217;s lack of explicit reasoning actually led to a <strong>more exploratory result</strong>&#8212;one that felt closer to something that might actually be submitted to VSDS at VividSeats.</p><p>That said, <strong>Claude 3.7 also skipped the compound component approach</strong>, allowing some logical flaws to slip through.</p><ul><li><p><strong>Approach #1</strong> is simple and straightforward, but the way spans are used makes it unclear what the <strong>actual implementation</strong> would accomplish without variants.</p></li><li><p><strong>Approach #2</strong> is cleaner, and while I initially questioned whether it was distinct enough, the switch from text child to a<strong> </strong><code>keys</code><strong> </strong>prop makes it a valid alternative.</p></li><li><p>However, the difference between<strong> </strong><code>key</code><strong> </strong>and<strong> </strong><code>keyBinding</code> is unclear beyond passing <code>keyBinding</code> to <code>formatKeyBinding()</code>. The extra structure for <strong>labelling modifiers</strong> also feels unnecessary.</p></li></ul><p>Even if modifiers are needed labels, I&#8217;d <strong>avoid passing objects through props</strong> when possible&#8212;key/value pairs usually should just be <strong>explicit props</strong> instead:</p><pre><code>&lt;Kbd modifier={['Ctrl', 'Alt']} key="Delete" /&gt;</code></pre><p>The best parts of this result are:<br>&#9989; <strong>Cross-platform considerations</strong><br>&#9989; <strong>Well-aligned ancillary resources</strong><br>&#9989; <strong>Better code quality</strong> (const assertions!?)</p><p>It would be even better if <code>KbdSize</code><strong> and </strong><code>KbdVariant</code> were inferred from the <code>KBD_SIZE</code> and <code>KBD_VARIANT</code> constants via <code>typeof</code>, but that&#8217;s something for review.</p><p>Overall, some details in the usage snippets are a bit dubious, but the output is <strong>solid enough</strong> to present. Now, let&#8217;s see what happens when we give Claude 3.7 more to work with!</p><div><hr></div><p><code>&lt;Breadcrumbs /&gt;</code><br>&#9888;&#65039; <strong>Questionable</strong></p><ul><li><p><strong>Pattern:</strong> Compound component</p></li><li><p><strong>Approach #1: Compound Component with Explicit Items</strong></p><ul><li><p><strong>Subcomponents:</strong> <code>Breadcrumbs.Item</code></p></li></ul></li></ul><pre><code>&lt;Breadcrumbs testId="site-breadcrumbs"&gt;
  &lt;Breadcrumbs.Item href="/"&gt;Home&lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Item href="/products"&gt;Products&lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Item href="/products/electronics"&gt;Electronics&lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Item active&gt;Smartphones&lt;/Breadcrumbs.Item&gt;
&lt;/Breadcrumbs&gt;</code></pre><ul><li><p><strong>Approach #2: Compound Component with Custom Separator</strong></p><ul><li><p><strong>Subcomponents:</strong> <code>Breadcrumbs.Item</code>, <code>Breadcrumbs.Separator</code></p></li></ul></li></ul><pre><code>&lt;Breadcrumbs 
  testId="custom-breadcrumbs"
  separator={&lt;Breadcrumbs.Separator&gt;/&lt;/Breadcrumbs.Separator&gt;}
&gt;
  &lt;Breadcrumbs.Item href="/"&gt;Home&lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Item href="/products"&gt;Products&lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Item active&gt;Categories&lt;/Breadcrumbs.Item&gt;
&lt;/Breadcrumbs&gt;</code></pre><ul><li><p><strong>Approach #3: Combination Support Built-in</strong></p></li></ul><pre><code><code>const items: BreadcrumbItem[] = [
  { label: 'Home', href: '/' },
  { label: 'Products', href: '/products' },
  { label: 'Electronics', href: '/products/electronics' },
  { label: 'Smartphones', active: true }
];

&lt;Breadcrumbs 
  testId="data-driven-breadcrumbs"
  items={items} 
/&gt;</code></code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Breadcrumbs</strong>: <code>maxDisplayed</code>, <code>items</code> (Approach #3), <code>separator</code>, <code>truncate</code>, <code>onCollapse()</code>, <code>onExpand()</code></p></li><li><p><strong>Breadcrumbs.Item</strong>: <code>active</code>, <code>href</code>, <code>icon</code>, <code>onPress()</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Hooks:</strong> <code>useBreadcrumbsContext()</code>, <code>useCollapsedItems()</code></p></li><li><p><strong>Functions:</strong> <code>formatBreadcrumbs()</code></p></li><li><p><strong>Types:</strong> <code>BreadcrumbItem</code>, <code>BreadcrumbsContextValue</code></p></li><li><p><strong>Enums:</strong> <code>BreadcrumbDisplayMode</code></p></li><li><p><strong>Constants: </strong><code>DEFAULT_SEPARATOR</code>, DEFAULT_MAX_DISPLAYED</p></li></ul></li></ul><h4>Review</h4><p><strong>Approach #1</strong> and <strong>Approach #3</strong> are both passable, <strong>Approach #2</strong> is not since it&#8217;s really just the same as <strong>Approach #1</strong> but with an extra subcomponent. Unfortunately, this is enough of a loss of credibility to keep this result in the <strong>Questionable</strong> category, though it&#8217;s presentable if we just <strong>remove Approach #2</strong>.</p><p>Again, the code snippets are much better than Claude 3.5. The hooks make more sense and they align with other resources (e.g. <code>useBreadcrumbsContext()</code> and <code>BreadcrumbsContextValue</code>.) Interestingly, Claude 3.7 also described the return type of <code>useCollapsedItems()</code>, which is an object that includes some state and a method <code>toggleCollapse()</code>.</p><p>For anyone curious about what <code>BreadcrumbDisplayMode</code> is, here you go!</p><pre><code>enum BreadcrumbDisplayMode {
  All = "all",
  Collapsed = "collapsed",
  FirstLast = "firstLast"
}</code></pre><div><hr></div><p><code>&lt;Table /&gt;</code> <br>&#9989; <strong>Passable Rough Draft</strong></p><ul><li><p><strong>Pattern:</strong> Compound component</p></li><li><p><strong>Variants:</strong></p></li><li><p><strong>Subcomponents:</strong></p></li><li><p><strong>Approach #1: Full Compound Structure with Context</strong></p></li></ul><pre><code>// Usage example
const BasicTable = () =&gt; (
  &lt;Table testId="user-data-table"&gt;
    &lt;Table.Caption&gt;List of active users&lt;/Table.Caption&gt;
    &lt;Table.Header&gt;
      &lt;Table.Row&gt;
        &lt;Table.HeaderCell&gt;Name&lt;/Table.HeaderCell&gt;
        &lt;Table.HeaderCell&gt;Email&lt;/Table.HeaderCell&gt;
        &lt;Table.HeaderCell&gt;Role&lt;/Table.HeaderCell&gt;
      &lt;/Table.Row&gt;
    &lt;/Table.Header&gt;
    &lt;Table.Body&gt;
      {users.map((user) =&gt; (
        &lt;Table.Row key={user.id}&gt;
          &lt;Table.Cell&gt;{user.name}&lt;/Table.Cell&gt;
          &lt;Table.Cell&gt;{user.email}&lt;/Table.Cell&gt;
          &lt;Table.Cell&gt;{user.role}&lt;/Table.Cell&gt;
        &lt;/Table.Row&gt;
      ))}
    &lt;/Table.Body&gt;
    &lt;Table.Footer&gt;
      &lt;Table.Row&gt;
        &lt;Table.Cell colSpan={3}&gt;Total Users: {users.length}&lt;/Table.Cell&gt;
      &lt;/Table.Row&gt;
    &lt;/Table.Footer&gt;
  &lt;/Table&gt;
);

// Sortable example
const SortableTable = () =&gt; {
  const [sortConfig, setSortConfig] = useState&lt;SortConfig&gt;({ key: 'name', direction: 'ascending' });
  
  const handleSort = (key: string) =&gt; {
    // Sort logic here
  };
  
  return (
    &lt;Table testId="sortable-user-table"&gt;
      &lt;Table.Header&gt;
        &lt;Table.Row&gt;
          &lt;Table.HeaderCell sortable onSort={() =&gt; handleSort('name')} sortDirection={sortConfig.key === 'name' ? sortConfig.direction : undefined}&gt;
            Name
          &lt;/Table.HeaderCell&gt;
          &lt;Table.HeaderCell sortable onSort={() =&gt; handleSort('email')} sortDirection={sortConfig.key === 'email' ? sortConfig.direction : undefined}&gt;
            Email
          &lt;/Table.HeaderCell&gt;
          &lt;Table.HeaderCell&gt;Role&lt;/Table.HeaderCell&gt;
        &lt;/Table.Row&gt;
      &lt;/Table.Header&gt;
      &lt;Table.Body&gt;
        {/* Sorted users */}
      &lt;/Table.Body&gt;
    &lt;/Table&gt;
  );
};</code></pre><ul><li><p><strong>Approach #2: Compositional with Column Definition</strong></p></li></ul><pre><code>// Usage example
const ColumnDefinedTable = () =&gt; {
  const columns: Array&lt;ColumnDefinition&gt; = [
    { key: 'name', header: 'Name', sortable: true },
    { key: 'email', header: 'Email', sortable: true },
    { key: 'role', header: 'Role' }
  ];
  
  return (
    &lt;Table 
      testId="user-table-defined" 
      columns={columns}
      data={users}
      caption="List of active users"
      onSort={(key, direction) =&gt; { /* Handle sort */ }}
    &gt;
      &lt;Table.Pagination 
        totalItems={users.length} 
        itemsPerPage={10} 
        onPageChange={(page) =&gt; { /* Handle page change */ }} 
      /&gt;
    &lt;/Table&gt;
  );
};

// With custom cell rendering
const CustomCellTable = () =&gt; {
  const columns: Array&lt;ColumnDefinition&gt; = [
    { 
      key: 'name', 
      header: 'Name', 
      render: (user) =&gt; &lt;strong&gt;{user.name}&lt;/strong&gt; 
    },
    { 
      key: 'status', 
      header: 'Status', 
      render: (user) =&gt; (
        &lt;Table.StatusCell status={user.status} /&gt;
      )
    },
    { 
      key: 'actions', 
      header: 'Actions', 
      render: (user) =&gt; (
        &lt;Table.ActionCell&gt;
          &lt;Button onPress={() =&gt; editUser(user)}&gt;Edit&lt;/Button&gt;
          &lt;Button onPress={() =&gt; deleteUser(user)}&gt;Delete&lt;/Button&gt;
        &lt;/Table.ActionCell&gt;
      )
    }
  ];
  
  return (
    &lt;Table 
      testId="custom-cell-table" 
      columns={columns}
      data={users}
    /&gt;
  );
};</code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Table</strong>: <code>columns</code>, <code>data</code> (Approach #2), <code>striped</code>, <code>bordered</code>, <code>compact</code>, <code>hoverable</code>, <code>sortConfig</code>,  <code>onSort()</code></p></li><li><p><strong>Table.Row</strong>: <code>selected</code>, <code>onPress()</code></p></li><li><p><strong>Table.Cell</strong>: <code>colSpan</code>, <code>rowSpan</code>, <code>align</code>, <code>truncate</code></p></li><li><p><strong>Table.HeaderCell</strong>: <code>colSpan</code>, <code>rowSpan</code>, <code>align</code>, <code>sortable</code>, <code>sortDirection</code>, <code>onSort()</code></p></li><li><p><strong>Table.Pagination</strong>: <code>currentPage</code>, <code>totalItems</code>, <code>itemsPerPage</code>, <code>onPageChange()</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Hooks:</strong> <code>useSortableTable()</code>, <code>useSelectableTable()</code>, <code>usePaginatedTable()</code>, <code>useTable()</code></p></li><li><p><strong>Functions:</strong> <code>sortTableData()</code>, <code>filterTableData()</code>, <code>paginateTableData()</code></p></li><li><p><strong>Types:</strong> <code>ColumnDefinition</code>, <code>SortConfig</code>, <code>SortableTableResult</code>, <code>SelectableTableOptions</code>, <code>SelectableTableResult</code>, <code>PaginationOptions</code>, <code>PaginatedTableResult</code>, <code>TableContext</code></p></li><li><p><strong>Enums:</strong> <code>SortDirection</code>, <code>TableCellAlignment</code></p></li><li><p><strong>Constants: </strong><code>DEFAULT_ITEMS_PER_PAGE</code>, <code>DEFAULT_PAGE_SIZES</code>, <code>DEFAULT_SORT_DIRECTION</code></p></li></ul></li></ul><h4><strong>Review</strong></h4><p>At first glance, both approaches feel <strong>over-engineered</strong>, and I wasn&#8217;t sure Approach #2 was distinct enough to be a true alternative. But looking closer, I noticed something I hadn&#8217;t seen before.</p><p>Compound components are <strong>only mutually exclusive to standalone components</strong>&#8212;they&#8217;re often used as <strong>base components</strong> both inside and outside the design system. When this happens <strong>within</strong> the design system, we call it a <strong>Compound-as-Base Hybrid</strong>.</p><p>Typically, compound components take subcomponents as children, but some cases <strong>invert this relationship</strong>, allowing a subcomponent (often a <code>Group</code>) to take the parent as children instead.</p><h4><strong>A New Pattern Emerges</strong></h4><p>Approach #2 does something interesting with <code>Table.ActionCell</code> and <code>Table.StatusCell</code>. Instead of using <code>Table.Cell</code> as just another subcomponent, <strong>Claude 3.7 treats it as a base subcomponent</strong>&#8212;essentially creating a base pattern within a compound component.</p><p>I&#8217;ve seen entire compound components used as base components, but I&#8217;ve never come across <strong>a compound component with a base subcomponent </strong>like this.</p><h4><strong>Potential Controversy</strong></h4><p>Is this a novel pattern or just so rare that I haven't encountered it before? Hard to say. Typically, <strong>base component variants</strong> are more <strong>generic</strong>&#8212;for example, atomic components like <code>Button</code> use system colors (<code>error</code>, <code>info</code>, <code>success</code>, <code>warning</code>, etc.) to compose specialized variants like <code>InfoButton</code>, <code>SuccessButton</code>, etc.</p><p>For <strong>more complex components</strong>, specialized variants are usually built <strong>outside</strong> the design system, composing agnostic design system components into <strong>business-specific</strong> variants&#8212;think a <code>Card</code> component spawning <code>NewsCard</code>, <code>BlogCard</code>, and <code>OfferCard</code>.</p><p>The biggest <strong>mistake</strong> teams make when building design systems is overfitting components to their specific business problems. And while it&#8217;s okay to tailor implementation details to company needs, a design system&#8217;s core purpose is to solve <strong>common, business-agnostic UI challenges</strong>.</p><p>A <strong>good design system</strong> should be flexible enough that if your org <strong>pivoted to a new industry</strong>, you could rebuild your platform using the <strong>same components</strong>. If your design system is <strong>entangled with business logic</strong>, you&#8217;re doing it wrong.</p><h4><strong>Applying This Pattern</strong></h4><p>This is where this <strong>new pattern</strong> could be controversial. For atomic components, specialization typically aligns with <strong>business-agnostic concerns</strong> like intent and system colors. But are<strong> </strong><code>Table.ActionCell</code><strong> </strong>and<strong> </strong><code>Table.StatusCell</code><strong> </strong>really business-agnostic enough to live in the design system?</p><p>Some might argue this makes the pattern an <strong>anti-pattern</strong>, but I see potential use cases&#8212;particularly in something like a <code>Form</code> component, where <code>Form.Input</code> could act as a base for specialized variants like <code>Form.NumericalInput</code>, <code>Form.ColorInput</code>, <code>Form.PhoneInput</code>, <code>Form.EmailInput</code>, etc.</p><p>Bringing this to the VSDS team would spark quite a debate&#8212;a roundtable discussion, if you will. And honestly? We love a good roundtable!</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/the-newest-version-of-claude-created-a-ui-pattern-ive-never-seen-before?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/the-newest-version-of-claude-created-a-ui-pattern-ive-never-seen-before?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you enjoyed this post, please subscribe, hit the &#10084;&#65039; button, and share/restack &#128257; it with others who might find it helpful!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p></p>]]></content:encoded></item><item><title><![CDATA[The AI Playbook for Design Systems: What Works, What Fails, and Why]]></title><description><![CDATA[How do ChatGPT o3, Claude 3.5 Sonnet, and DeepSeek R1 compare when planning enterprise-strength design system components? The results shocked me!]]></description><link>https://blog.robhameetman.com/p/ai-driven-code-mockups</link><guid isPermaLink="false">https://blog.robhameetman.com/p/ai-driven-code-mockups</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Tue, 04 Feb 2025 10:46:24 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6638f075-6915-406a-85a6-c13fc5ad3bfe_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!esCa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!esCa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!esCa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!esCa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!esCa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!esCa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:817200,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154956189?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!esCa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!esCa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!esCa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!esCa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff8752c7a-c861-4b22-86e5-c5b6bb12677a_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>A Refresher On Code Mockups</h2><p>Last week, I introduced <strong><a href="https://blog.robhameetman.com/p/how-to-write-a-code-mockup?r=2ypy2w">code mockups</a></strong>&#8212;a structured approach to designing UI components before they&#8217;re built. This method improves <strong>scalability and maintainability</strong>, but it&#8217;s time-consuming, requiring deep exploration, iteration, and validation.</p><p>AI has the potential to change that. By <strong>automating pattern exploration, generating usage examples, and enforcing best practices</strong>, Engineers can iterate on <strong>AI-assisted scaffolding</strong>&#8212;focusing on <strong>high-impact decisions instead of repetitive setup</strong>.</p><p>In this article, I&#8217;ll explore how AI enhanced the <strong><a href="https://blog.robhameetman.com/p/how-to-write-a-code-mockup?r=2ypy2w">code mockup workflow</a></strong>, making it faster, more efficient, and accessible. Plus, I&#8217;ll share how we&#8217;re applying it at <strong>Vivid Seats</strong> to streamline design system development.</p><p>Let&#8217;s dive in.</p><div><hr></div><h2>What Are We Working With?</h2><div><hr></div><h3>1. The Workflow</h3><p>Copy everything from <em><a href="https://blog.robhameetman.com/i/154924704/code-mockups-for-enterprise-strength-design-system-components">Code Mockups For Enterprise-Strength Design System Components</a></em> to the end and paste it in <strong>The Prompt</strong>, replacing <code>{{WORKFLOW}}</code>. Remember to strip out any promotional sections.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;dc35e2c5-51d0-426a-b6df-cd4f5cf1fcfd&quot;,&quot;caption&quot;:&quot;Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;How To Write A Code Mockup&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:179249864,&quot;name&quot;:&quot;Rob Hameetman&quot;,&quot;bio&quot;:&quot;I build Design Systems and Micro-frontends in TypeScript and React with a focus on developer experience / Staff Frontend Engineer / TED curator / Ex-Apple (HIG) / 1.2M+ subs&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fd162dbf-8c1b-48bc-b6e7-0969af7da7ca_475x475.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-01-23T11:33:32.814Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e525a0d0-6699-4e01-9dad-7c3a58d94570_2048x2048.webp&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://blog.robhameetman.com/p/how-to-write-a-code-mockup&quot;,&quot;section_name&quot;:&quot;Resources&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:154924704,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;UI Engineering Excellence&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!deTI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc5e8141-3a0f-4181-8804-acdbb61e8707_359x359.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><h3>2. The Template</h3><p>Copy everything in the code block below and paste it in <strong>The Prompt</strong>, replacing <code>{{TEMPLATE}}</code>:</p><pre><code><code># Code Mockup: &lt;Component /&gt; [Template]

**Category:**

**Pattern:**

Choose One:

- Standalone Component
- Compound Component
- Base Component
- Compound-as-Base-Hybrid

**Key Stakeholders:**

- Recommend: _Your Name_
- Sponsor: _Design System Maintainer Name_
- Implement: _The Engineer doing the work if known_

&gt; **High-level Details**
&gt; _The code below is meant to give an idea of what the implementation in
&gt; Next might look like from a birds-eye view. It is not meant to (and
&gt; will not) be 100% correct; actual implementation details may vary._

## Pattern Notes

&gt; **DELETE BEFORE REVIEW**
&gt; _Add any details to this section that explain alterations of the
&gt; pattern or ways we might expect the pattern to evolve or change as
&gt; this component scales in complexity. Remove the entire section if you
&gt; have no notes prior to review._

## Variants

&gt; **DELETE BEFORE REVIEW**
&gt; _Delete this entire section if this is not a **Compound Component** or
&gt; **Compound-as-Base-Hybrid**. Otherwise, rename the below keeping the
&gt; naming convention for **Base Components** using the name of the base
&gt; as the suffix in variant names (e.g. variants of a base component
&gt; `Button` should be named `ThisButton`, `ThatButton`, etc.)_

Component will be used as a base in the following shared components:

- `VariantComponent`
- `VariantComponent`
- `VariantComponent`
- `VariantComponent`

## Composition Strategy

&gt; **DELETE BEFORE REVIEW**
&gt; _Add mocked up component usage code snippets in this section._

### Approach #1:

### Approach #2:

## Props

### Component

&gt; **DELETE BEFORE REVIEW**
&gt; _Add to the following set of minimum required props. Check our prop
&gt; standards to ensure consistency in our component contracts.
&gt; Review guidelines to ensure props avoid becoming interdependent.
&gt; Leave comments or footnotes exploring alternative approaches for
&gt; specific prop(s) as needed._

| PROP      | TYPE                                        | USAGE    |
| --------- | ------------------------------------------- | -------- |
| as        | `keyof JSX.IntrinsicElements | ElementType` | OPTIONAL |
| className | `string`                                    | OPTIONAL |
| error     | `Error  | null`                             | OPTIONAL |
| loading   | `boolean`                                   | OPTIONAL |
| testId    | `string`                                    | OPTIONAL |

### Component.SubComponent

&gt; **DELETE BEFORE REVIEW**
&gt; _Add to the following set of minimum required props. Check our prop
&gt; standards to ensure consistency in our component contracts.
&gt; Review guidelines to ensure props avoid becoming interdependent.
&gt; Leave comments or footnotes exploring alternative approaches for
&gt; specific prop(s) as needed._

| PROP      | TYPE                                        | USAGE    |
| --------- | ------------------------------------------- | -------- |
| as        | `keyof JSX.IntrinsicElements | ElementType` | OPTIONAL |
| className | `string`                                    | OPTIONAL |
| error     | `Error  | null`                             | OPTIONAL |
| loading   | `boolean`                                   | OPTIONAL |
| testId    | `string`                                    | OPTIONAL |

&gt; **DELETE BEFORE REVIEW**
&gt; _Add one of these sections for each subcomponent if the component is a
&gt; **Compound Component** or **Compound-As-Base-Hybrid**. Remove this
&gt; entire section if the component is a **Standard Component** or **Base
&gt; Component**._

&gt; **DELETE BEFORE REVIEW**
&gt; _The following section(s) are optional but recommended so that we
&gt; don&#8217;t keep reinventing wheels._

## Utils

### Hooks

### Functions

### Types &amp; Interfaces

### Enums &amp; Constants</code></code></pre><div><hr></div><h3>3. The Prompt</h3><ul><li><p><code>[text surrounded by brackets]</code> is optional.</p></li><li><p><code>{{TEXT}}</code> using handlebars syntax indicates where specific content should be injected.</p></li><li><p><code>(pick one: foo|bar|baz)</code> indicates where you should pick one of the provided options.</p></li></ul><pre><code><code>Write a Code Mockup for a new &lt;Component /&gt; component[ implemented as a (pick: standard|compound|base) component].

Here's the workflow for writing a Code Mockup:

"{{WORKFLOW}}"

Here is the list of all Standardized Props for context:

| PROP            | TYPE                                         | USAGE    |
| --------------- | -------------------------------------------- | -------- |
| as              | `keyof JSX.IntrinsicElements \| ElementType` | OPTIONAL |
| animated        | `boolean`                                    | OPTIONAL |
| className       | `string`                                     | OPTIONAL |
| children        | ReactNode                                    | OPTIONAL |
| disabled        | `boolean`                                    | OPTIONAL |
| error           | `Error  \| null`                             | OPTIONAL |
| fullWidth       | `boolean`                                    | OPTIONAL |
| loading         | `boolean`                                    | OPTIONAL |
| testId          | `string`                                     | REQUIRED |
| onPress()       | `PressEventHandler`                          | OPTIONAL |
| onPressEnter()  | `EnterEventHandler`                          | OPTIONAL |
| onPressEscape() | `EscapeEventHandler`                         | OPTIONAL |
| onPressSpace()  | `SpaceEventHandler`                          | OPTIONAL |
| onPressTab()    | `TabEventHandler`                            | OPTIONAL |
| onTabBack()     | `TabBackEventHandler`                        | OPTIONAL |


Use this template for the Code Mockup:

```Markdown
{{TEMPLATE}}
```

[Optionally, add a short description of the differing approaches you're looking into in your mockups. Design Responsibility? Splitting up components? Any other details that are relevant?]

The response may be output as regular text; markdown from the template may be applied instead of preserved. Do not complete the Key Stakeholders section, as it will be filled out later. Remember that code mockups for design system components are about abstracting the underlying mechanics, so do not generate any code which includes implementation details of this component or its potential ancillary dependencies. Utility functions, hooks, enums, constants, etc should be described in juxtaposition to each other with regard to potential approaches. Prop and ancillary types and interfaces are an exception to this rule, since they are separate from implementation. Remember when naming types and interfaces that they should not know they are types and interfaces respectively, so do not prefix names with `T` or `I`, nor include any other such redundancy like ending the name with `Type`, or `Data` for types/interfaces that represent data. Generics and type parameters should use capitalized single letter names from the letter `T`, incrementing one letter of the alphabet for each additional generic or type parameter. Arrays should use generic syntax (i.e. `Array&lt;T&gt;` instead of `T[]`.) Do not use object literal types unless writing a mapped type, since index mapping and key remapping are not allowed in interfaces. Properties in interfaces should be `readonly` except in cases where they are intentionally mutable; likewise, arrays should be typed as `ReadonlyArray`s unless they are intentionally mutated in place. Destructure type and value imports from React rather than using the `React` namespace with a default import named React. Do not use "style" props like `marginTop` or `border` unless they are already included on the list of Standardized Props. Keep usage snippets as focused as possible, and do not include surrounding boilerplate unless absolutely necessary.</code></code></pre><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>&#10084;&#65039; Like what you&#8217;re seeing so far?</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>The Results</h2><p>I trialed and refined this process with multiple models to see which provides the best jumping-off point, and the results were <strong>not</strong> what I was expecting.</p><p>I wanted to see how AI would handle heterogenous levels of complexity, so I experimented with the following components:</p><ul><li><p><code>&lt;Kbd /&gt;</code><br>An atomic component so simple one might wonder why it exists at all when one could just use the <code>&lt;kbd&gt;</code> HTML element directly.</p></li><li><p><code>&lt;Breadcrumbs /&gt;</code><br>A molecule component just complex enough that it can be implemented with any of the three main patterns described in <strong>The Workflow</strong>.</p></li><li><p><code>&lt;Table /&gt;</code><br>An organism component too complex to be anything other than a compound component.</p></li></ul><p>The only prompt which specified the pattern was the prompt for the Table component. Other than this, I was hands off and did not add any context or details about how the component could or should be built.</p><div><hr></div><h3>ChatGPT o3</h3><div><hr></div><p><code>&lt;Kbd /&gt;</code>  -  &#9888;&#65039; <strong>Questionable<br></strong><em>ChatGPT o3</em></p><ul><li><p><strong>Pattern:</strong> Standalone component</p></li><li><p><strong>Approach #1:</strong> <strong>Basic Usage</strong></p></li></ul><pre><code>&lt;Kbd testId="kbd-basic"&gt;Enter&lt;/Kbd&gt;</code></pre><ul><li><p><strong>Approach #2: Composite Key Sequence</strong></p></li></ul><pre><code>&lt;Kbd className="custom-kbd" testId="kbd-sequence"&gt;
  &lt;span&gt;Ctrl&lt;/span&gt; + &lt;span&gt;Shift&lt;/span&gt; + &lt;span&gt;Esc&lt;/span&gt;
&lt;/Kbd&gt;</code></pre><ul><li><p><strong>No discrete props</strong></p></li><li><p><strong>No ancillary resources</strong></p></li></ul><h4>Review</h4><p><strong>Approach #1</strong> closely follows the behavior of the <code>&lt;kbd&gt;</code> HTML element, making it a familiar and intuitive choice.</p><p><strong>Approach #2</strong> is interesting because it supports multiple keys, though the usage snippet demonstrates bad technique by wrapping keys in spans. </p><p>It also looks like the AI misunderstood the <strong>testId</strong> prop being marked as <strong>REQUIRED</strong> in the Standardized Props table. This designation simply means the component must <strong>support</strong> <code>testId</code>, not that every instance <strong>must</strong> include it. In general, props&#8212;especially standardized ones&#8212;should rarely be <strong>mandatory</strong> unless there's a compelling reason to enforce their usage.</p><div><hr></div><p><code>&lt;Breadcrumbs /&gt;</code>  -  &#10060; <strong>Not Passable<br></strong><em>ChatGPT o3</em></p><ul><li><p><strong>Pattern:</strong> Compound component</p></li><li><p><strong>Subcomponents:</strong> <code>Breadcrumbs.Item</code></p></li><li><p><strong>Approach #1: </strong><em>(the AI did not name either approach)</em></p></li></ul><pre><code>&lt;Breadcrumbs testId="breadcrumbs"&gt;
  &lt;Breadcrumbs.Item as="a" href="/" testId="breadcrumb-home"&gt;
    Home
  &lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Item as="a" href="/products" testId="breadcrumb-products"&gt;
    Products
  &lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Item testId="breadcrumb-current"&gt;
    Current Page
  &lt;/Breadcrumbs.Item&gt;
&lt;/Breadcrumbs&gt;</code></pre><ul><li><p><strong>Approach #2:</strong></p></li></ul><pre><code>const breadcrumbItems = [
  { label: 'Home', href: '/', testId: 'breadcrumb-home' },
  { label: 'Products', href: '/products', testId: 'breadcrumb-products' },
  { label: 'Current Page', testId: 'breadcrumb-current' },
];

&lt;Breadcrumbs testId="breadcrumbs" className="custom-breadcrumbs"&gt;
  {breadcrumbItems.map((item, index) =&gt; (
    &lt;Breadcrumbs.Item 
      key={index}
      as={item.href ? 'a' : 'span'}
      href={item.href}
      testId={item.testId}
      disabled={item.disabled}
    &gt;
      {item.label}
    &lt;/Breadcrumbs.Item&gt;
  ))}
&lt;/Breadcrumbs&gt;</code></pre><ul><li><p><strong>No discrete props</strong></p></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Hooks:</strong> <code>useBreadcrumbsContext()</code> and an optional <code>useResponsiveBreadcrumbs()</code> hook to handle dynamic layout adjustments</p></li><li><p><strong>Functions:</strong> <code>formatBreadcrumbs()</code></p></li><li><p><strong>Types:</strong> <code>BreadcrumbsProps</code>, <code>BreadcrumbItemProps</code></p></li><li><p><strong>Constants: </strong><code>BREADCRUMBS_SEPARATOR</code></p></li></ul></li></ul><h4>Review</h4><p>This fails because it&#8217;s essentially the <strong>same approach</strong> served two different ways. </p><p>The <strong>subcomponent naming</strong> is lacking in refinement&#8212;<code>Breadcrumbs.Item</code> feels okay but a bit generic and uninspired. A more <strong>semantic</strong> choice like <code>Breadcrumbs.Crumb</code> or a more <strong>streamlined</strong> option like <code>Breadcrumbs.Link</code> would improve clarity and usability.</p><p>Then there&#8217;s <code>formatBreadcrumbs()</code>&#8212;which, as far as I can tell, isn&#8217;t even being used. The output describes it as a function that converts data into an array of <code>Breadcrumbs.Item</code> subcomponents, but the usage snippets either handle this conversion manually or skip it altogether by passing subcomponents as children. Even if it <strong>were</strong> used, it should probably be a <strong>custom hook</strong> since it&#8217;s returning <strong>presentation logic</strong>, not just transformed data.</p><div><hr></div><p><code>&lt;Table /&gt;</code>  -  &#10060; <strong>Not Passable<br></strong><em>ChatGPT o3</em></p><ul><li><p><strong>Pattern:</strong> Compound component (the output implies more of a <em>Compound-as-Base Hybrid</em> by including specialized variants)</p></li><li><p><strong>Variants:</strong> <code>DataTable</code>, <code>SortableTable</code>, <code>PaginatedTable</code>, <code>ResponsiveTable</code></p></li><li><p><strong>Subcomponents:</strong> <code>Table.Header</code>, <code>Table.Row</code>, <code>Table.Cell</code>, <code>Table.Body</code>, <code>Table.Footer</code>, <code>Table.Caption</code></p></li><li><p><strong>Approach #1: Basic Table Structure</strong></p></li></ul><pre><code>&lt;Table testId="basic-table" className="custom-table"&gt;
  &lt;Table.Header&gt;
    &lt;Table.Row&gt;
      &lt;Table.Cell as="th"&gt;Name&lt;/Table.Cell&gt;
      &lt;Table.Cell as="th"&gt;Age&lt;/Table.Cell&gt;
      &lt;Table.Cell as="th"&gt;Location&lt;/Table.Cell&gt;
    &lt;/Table.Row&gt;
  &lt;/Table.Header&gt;
  &lt;Table.Body&gt;
    &lt;Table.Row&gt;
      &lt;Table.Cell&gt;Jane Doe&lt;/Table.Cell&gt;
      &lt;Table.Cell&gt;29&lt;/Table.Cell&gt;
      &lt;Table.Cell&gt;New York&lt;/Table.Cell&gt;
    &lt;/Table.Row&gt;
    &lt;Table.Row&gt;
      &lt;Table.Cell&gt;John Smith&lt;/Table.Cell&gt;
      &lt;Table.Cell&gt;34&lt;/Table.Cell&gt;
      &lt;Table.Cell&gt;San Francisco&lt;/Table.Cell&gt;
    &lt;/Table.Row&gt;
  &lt;/Table.Body&gt;
&lt;/Table&gt;</code></pre><ul><li><p><strong>Approach #2:</strong> <strong>Table with Footer and Optional Caption</strong></p><p>I decided to omit the code snippet for this to save space since its the same as the above but with:</p></li></ul><pre><code>&lt;Table.Caption&gt;Employee Directory&lt;/Table.Caption&gt;</code></pre><ul><li><p><strong>No discrete props</strong></p></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Hooks:</strong> <code>useTableSort()</code> and an optional <code>useTablePagination()</code> hook</p></li><li><p><strong>Functions:</strong> <code>calculateColumnWidths()</code>, <code>mergeCellStyles()</code></p></li><li><p><strong>Types:</strong> Prop types for each subcomponent</p></li><li><p><strong>Enums:</strong> <code>TableSortDirection</code> (e.g. <code>ASC</code> and <code>DESC</code>)</p></li><li><p><strong>Constants: </strong><code>DEFAULT_CELL_PADDING</code></p></li></ul></li></ul><h4>Review</h4><p>This fails pretty much right off the bat because the usage snippets show <code>Table.Header</code> and <code>Table.Footer</code> in both approaches but the title of <strong>Approach #2</strong> implies that basic table structure should not include a header and footer.</p><p>Also, only standardized props? No discrete props? Not even a <code>sortOrder</code> prop that uses a <code>TableSortDirection</code>?</p><div><hr></div><h3>Claude 3.5 Sonnet</h3><div><hr></div><p><code>&lt;Kbd /&gt;</code>  -  &#9989; <strong>Passable Rough Draft<br></strong><em>Claude 3.5 Sonnet</em></p><ul><li><p><strong>Pattern: </strong>Standalone or Compound Component</p></li><li><p><strong>Approach #1: Single Component with Key Combinations</strong></p></li></ul><pre><code>// Individual key
&lt;Kbd&gt;&#8984;&lt;/Kbd&gt;

// Key combination
&lt;Kbd&gt;&#8984; + K&lt;/Kbd&gt;

// Sequential combination
&lt;Kbd&gt;g g&lt;/Kbd&gt;

// With custom separator
&lt;Kbd separator="+"&gt;&#8984; K&lt;/Kbd&gt;

// Nested in text
Press &lt;Kbd&gt;&#8984;&lt;/Kbd&gt; + &lt;Kbd&gt;K&lt;/Kbd&gt; to open the command palette</code></pre><ul><li><p><strong>Approach #2: Compound Component with Specialized Parts</strong></p><ul><li><p><strong>Subcomponents:</strong> <code>Kbd.Key</code>, <code>Kbd.Group</code>, <code>Kbd.Separator</code></p></li></ul></li></ul><pre><code>// Individual key
&lt;Kbd.Key&gt;&#8984;&lt;/Kbd.Key&gt;

// Key combination
&lt;Kbd.Group&gt;
  &lt;Kbd.Key&gt;&#8984;&lt;/Kbd.Key&gt;
  &lt;Kbd.Separator&gt;+&lt;/Kbd.Separator&gt;
  &lt;Kbd.Key&gt;K&lt;/Kbd.Key&gt;
&lt;/Kbd.Group&gt;

// Sequential combination
&lt;Kbd.Group sequential&gt;
  &lt;Kbd.Key&gt;g&lt;/Kbd.Key&gt;
  &lt;Kbd.Key&gt;g&lt;/Kbd.Key&gt;
&lt;/Kbd.Group&gt;</code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Kbd</strong>: <code>separator</code>, <code>size</code>, <code>variant</code></p></li><li><p><strong>Kbd.Group</strong>: <code>sequential</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Types: </strong>Prop types for the component in <strong>Approach #1</strong> and for each subcomponent in <strong>Approach #2</strong> except for <code>Kbd.Separator</code></p></li><li><p><strong>Enums:</strong> <code>KbdSize</code>, <code>KbdVariant</code></p></li><li><p><strong>Constants:</strong> <code>DEFAULT_KBD_SEPARATOR</code></p></li></ul></li></ul><h4>Review</h4><p>The difference in quality between this and ChatGPT o3 is Grand Canyon huge. This is mind-blowingly better. Multiple patterns, discrete props, utils, the works! On a component this simple, output this good can be used with only minor refinement.</p><p>If an Engineer at Vivid Seats brought this forward for review with the VSDS team, my main feedback would be that <code>Kbd.Key</code> should just be the parent <code>Kbd</code> so we <strong>don&#8217;t bloat complexity</strong> with redundant subcomponents. While compound components typically take multiple subcomponents as children, in some cases they can use an <strong>inverted form</strong> which allows for a subcomponent (typically <code>Group</code>) to take the parent as children instead.</p><p>I would also dig in a bit to understand why including variants is necessary to meet requirements for this component. It&#8217;s not out of the realm of possibility, but without a mockup backing it up it <strong>feels over-Engineered</strong>. If we actually have a business case for <code>KbdVariants</code>, then naturally a question of whether we should consider a <strong>Base Component pattern</strong> for these variants would follow.</p><p>Also, the Kbd.<code>Separator</code> subcomponent is unnecessary. Given the way keyboard commands <strong>chain together</strong>, we have no reason not to assume that &#8220;+&#8221; should be the operator until or unless business requirements make it clear otherwise. That said, if we kept this subcomponent, it should use a <strong>string-only semantic </strong><code>as</code><strong> prop</strong> instead of accepting a text child each time so that we can rely on <code>DEFAULT_KBD_SEPARATOR</code> unless we need to override it.</p><pre><code>&lt;Kbd.Separator as="+" /&gt;</code></pre><p>Lastly, <code>size</code> should be <code>fontSize</code>, if we include a specialized prop for this at all. As an inline element, <strong>it&#8217;s size should scale conformally</strong> with the size of the text it contains, so there&#8217;s no need for an enum like <code>KbdSize</code>.</p><p>I&#8217;d probably still lean towards <strong>Approach #1</strong>, but this at least gave us an idea of what another possibility might look like, even if just to confirm it&#8217;s too much. All in all, a very impressive jumping-off point.</p><div><hr></div><p><code>&lt;Breadcrumbs /&gt;</code>  -  &#9989; <strong>Passable Rough Draft<br></strong><em>Claude 3.5 Sonnet</em></p><ul><li><p><strong>Pattern: </strong>Standalone or Compound Component</p></li><li><p><strong>Approach #1: Data-Driven Composition</strong></p></li></ul><pre><code>&lt;Breadcrumbs
  items={[
    { label: "Dashboard", href: "/dashboard" },
    { label: "Projects", href: "/dashboard/projects" },
    { label: "Project Details" }
  ]}
/&gt;</code></pre><ul><li><p><strong>Approach #2: Explicit Composition</strong></p><ul><li><p><strong>Subcomponents:</strong> <code>Breadcrumbs.Item</code>, <code>Breadcrumbs.Separator</code></p></li></ul></li></ul><pre><code>&lt;Breadcrumbs&gt;
  &lt;Breadcrumbs.Item&gt;
    &lt;Link href="/dashboard"&gt;Dashboard&lt;/Link&gt;
  &lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Separator&gt;/&lt;/Breadcrumbs.Separator&gt;
  &lt;Breadcrumbs.Item&gt;
    &lt;Link href="/dashboard/projects"&gt;Projects&lt;/Link&gt;
  &lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Separator&gt;/&lt;/Breadcrumbs.Separator&gt;
  &lt;Breadcrumbs.Item aria-current="page"&gt;
    Project Details
  &lt;/Breadcrumbs.Item&gt;
&lt;/Breadcrumbs&gt;</code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Breadcrumbs</strong>: <code>separator</code></p></li><li><p><strong>Breadcrumbs.Item</strong>: <code>current</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Hooks:</strong> <code>useBreadcrumbs()</code> vs <code>useRouteAsBreadcrumbs(</code>)</p></li><li><p><strong>Functions:</strong> <code>createBreadcrumbsFromPath()</code> vs <code>createBreadcrumbsFromRoutes()</code></p></li><li><p><strong>Types:</strong> <code>BreadcrumbItem</code>, <code>BreadcrumbContext</code></p></li></ul></li></ul><h4>Review</h4><p>Some criticism from the <code>Kbd</code> component carry over, like <code>Breadcrumbs.Separator</code> using children instead of a prop as needed. But the output is, again, miles better than ChatGPT o3.</p><p>The best thing about this output is that it includes <code>Link</code>, showing how we can use other components compositionally. It also clearly juxtaposes two approaches for hooks and reflects the potential downstream impact of these approaches in subsequent utility functions.</p><p>Claude 3.5 Sonnet also used the <em>Pattern Notes</em> section to outline four ways we might want to change or improve the component in the future.</p><p>I would lean towards <strong>Approach #2</strong> and give it a semantic <code>as</code> prop so that we can streamline the usage like this:</p><pre><code>&lt;Breadcrumbs&gt;
  &lt;Breadcrumbs.Item as={Link} href="/dashboard"&gt;
    Dashboard
  &lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Separator /&gt;
  &lt;Breadcrumbs.Item as={Link} href="/dashboard/projects"&gt;
    Projects
  &lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Separator /&gt;
  &lt;Breadcrumbs.Item aria-current="page"&gt;
    Project Details
  &lt;/Breadcrumbs.Item&gt;
&lt;/Breadcrumbs&gt;</code></pre><div><hr></div><p><code>&lt;Table /&gt;</code>  -  &#9888;&#65039; <strong>Questionable<br></strong><em>Claude 3.5 Sonnet</em></p><ul><li><p><strong>Pattern:</strong> Compound Component</p></li><li><p><strong>Subcomponents:</strong> Table.Header, Table.Body, Table.Row, Table.Body, Table.Pagination</p></li><li><p><strong>Approach #1: Explicit Structure</strong></p></li></ul><pre><code>&lt;Table&gt;
  &lt;Table.Header&gt;
    &lt;Table.Row&gt;
      &lt;Table.HeaderCell sortable&gt;Name&lt;/Table.HeaderCell&gt;
      &lt;Table.HeaderCell&gt;Email&lt;/Table.HeaderCell&gt;
      &lt;Table.HeaderCell align="right"&gt;Status&lt;/Table.HeaderCell&gt;
    &lt;/Table.Row&gt;
  &lt;/Table.Header&gt;
  &lt;Table.Body&gt;
    &lt;Table.Row&gt;
      &lt;Table.Cell&gt;Jane Cooper&lt;/Table.Cell&gt;
      &lt;Table.Cell&gt;jane@example.com&lt;/Table.Cell&gt;
      &lt;Table.Cell align="right"&gt;Active&lt;/Table.Cell&gt;
    &lt;/Table.Row&gt;
  &lt;/Table.Body&gt;
  &lt;Table.Footer&gt;
    &lt;Table.Pagination /&gt;
  &lt;/Table.Footer&gt;
&lt;/Table&gt;</code></pre><ul><li><p><strong>Approach #2: Data-Driven</strong></p></li></ul><pre><code>&lt;Table 
  data={users}
  columns={[
    { key: 'name', header: 'Name', sortable: true },
    { key: 'email', header: 'Email' },
    { key: 'status', header: 'Status', align: 'right' }
  ]}
&gt;
  &lt;Table.Header /&gt;
  &lt;Table.Body&gt;
    {(row) =&gt; (
      &lt;Table.Row key={row.id}&gt;
        &lt;Table.Cell&gt;{row.name}&lt;/Table.Cell&gt;
        &lt;Table.Cell&gt;{row.email}&lt;/Table.Cell&gt;
        &lt;Table.Cell align="right"&gt;{row.status}&lt;/Table.Cell&gt;
      &lt;/Table.Row&gt;
    )}
  &lt;/Table.Body&gt;
  &lt;Table.Pagination /&gt;
&lt;/Table&gt;</code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Table</strong>: <code>data</code>, <code>columns</code>, <code>sortable</code></p></li><li><p><strong>Table.Header</strong>: <code>sticky</code></p></li><li><p><strong>Table.HeaderCell</strong>: <code>align</code>, <code>sortable</code>, <code>sortDirection</code>, <code>onSort</code></p></li><li><p><strong>Table.Row</strong>: <code>selected</code></p></li><li><p><strong>Table.Pagination</strong>: <code>page</code>, <code>pageSize</code>, <code>onPageChange()</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Events:</strong> <code>SortEvent</code>, <code>SortEventHandler</code>, <code>PageChangeEvent</code>, <code>PageChangeEventHandler</code></p></li><li><p><strong>Hooks:</strong> useTableSort(), useTablePagination(), useTableSelection(), useTableFilters()</p></li><li><p><strong>Types:</strong> <code>Column</code>, <code>TableData</code>, <code>SortState</code>, <code>Alignment</code>, <code>SortDirection</code>, <code>RenderFunction&lt;T&gt;</code></p></li></ul></li></ul><h4>Review</h4><p>The output for this one reaches the limits I think for what AI will be able to do here on its own, at least for now.</p><p>These two approaches aren&#8217;t really mutually exclusive, you&#8217;ll probably end up doing both depending on how the component is used throughout the app. Better approaches would be to compare<strong> heterogeneous levels of design responsibility</strong>, or alternatively juxtaposing <strong>a basic compound component</strong> with no pagination, sorting, filtering, etc <strong>against a more complex version</strong> with this more advanced functionality to get a sense of how the component might scale or be built iteratively.</p><p>And this is why Tables are <em>hard</em>. At its core, the <code>Table</code> component operates as an <strong>ordered</strong> <strong>functional pipeline</strong>, where raw data flows through a series of <strong>pure transformations</strong> in a specific order before rendering. Each transformation is a function that takes the current dataset and returns a new, modified version:</p><pre><code>paginate(sort(filter(search(data))))</code></pre><p>We see a hook for filtering, but only sorting seems to be really thought out.</p><p>The best part of this output is that there are <strong>custom events</strong>! Custom events are also hard because they require special handling, don&#8217;t propagate predictably, and break framework optimizations. Instead of subclassing <code>Event</code> or using <code>CustomEvent</code>, extend native event types by <strong>defining subtypes in TypeScript</strong> for safer, more predictable behavior. Since we know that <code>SortEvent</code> and <code>PageChangeEvent</code> would be dispatched by pressing a button, we should type them like this:</p><pre><code>type SortEvent&lt;T&gt; = PointerEvent&lt;T&gt;
type SortEventHandler&lt;T&gt; = PointerEventHandler&lt;T&gt;

type PageChangeEvent&lt;T&gt; = PointerEvent&lt;T&gt;
type PageChangeEventHandler&lt;T&gt; = PointerEventHandler&lt;T&gt;</code></pre><p>In VSDS, we have a custom <code>PressEvent</code> to make it <strong>easier to share code</strong> between Mobile and Web, so we would create subtypes of that instead. The output, however, included only basic interfaces for these events.</p><pre><code>interface SortEvent {
  readonly column: string
  readonly direction: SortDirection
}

interface PageChangeEvent {
  readonly page: number
  readonly pageSize: number
}</code></pre><p>This might be a quirk we can iron out with more <strong>explicit direction</strong> in the prompt, but for now it&#8212; combined with the rudimentary approaches are enough to put this one in the <strong>Questionable</strong> category, but not at all a bad start!</p><div><hr></div><h3>DeepSeek R1</h3><div><hr></div><p><code>&lt;Kbd /&gt;</code>  -  &#9888;&#65039; <strong>Questionable<br></strong><em>DeepSeek R1</em></p><ul><li><p><strong>Pattern: </strong>Standalone Component</p></li><li><p><strong>Approach #1: Basic Semantic Usage</strong></p></li></ul><pre><code>// Single key
&lt;Kbd testId="enter-key"&gt;Enter&lt;/Kbd&gt;

// Disabled state
&lt;Kbd disabled testId="disabled-key"&gt;&#8984;&lt;/Kbd&gt;

// Custom element with animation
&lt;Kbd as="span" animated testId="animated-key"&gt;Tab&lt;/Kbd&gt;</code></pre><ul><li><p><strong>Approach #2: Keyboard Shortcut Grouping</strong></p></li></ul><pre><code>// Composition with other elements
&lt;div className="shortcut-group"&gt;
  &lt;Kbd testId="mod1"&gt;Ctrl&lt;/Kbd&gt; + 
  &lt;Kbd testId="mod2"&gt;Alt&lt;/Kbd&gt; + 
  &lt;Kbd testId="main-key"&gt;Del&lt;/Kbd&gt;
&lt;/div&gt;</code></pre><ul><li><p><strong>No Discrete Props</strong></p></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Types: </strong><code>StandardKbdProps</code></p></li></ul></li></ul><h4>Review</h4><p>This output feels a lot closer to ChatGPT o3. It doesn&#8217;t actually give us two different approaches, just <strong>two different use-cases</strong>. Most interestingly though is that, according to DeepSeek&#8217;s <strong>exposed inner monologue</strong>, it briefly considered a compound component before deciding not to explore that pattern. This makes me think that with a bit of massaging, the prompt could be Engineered to yield more exploratory results.</p><p>We don&#8217;t really need more than one pattern here, so this output is <strong>Questionable</strong> at best.</p><div><hr></div><p><code>&lt;Breadcrumbs /&gt;</code>  -  &#9989; <strong>Passable Rough Draft<br></strong><em>DeepSeek R1</em></p><ul><li><p><strong>Pattern: </strong>Compound Component</p></li><li><p><strong>Approach #1: Explicit Composition</strong></p><ul><li><p><strong>Subcomponents:</strong> <code>Breadcrumbs.Item</code>, <code>Breadcrumbs.Separator</code></p></li></ul></li></ul><pre><code>&lt;Breadcrumbs testId="nav-trail"&gt;
  &lt;Breadcrumbs.Item href="/"&gt;Home&lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Separator /&gt;
  &lt;Breadcrumbs.Item href="/products"&gt;Products&lt;/Breadcrumbs.Item&gt;
  &lt;Breadcrumbs.Separator&gt;
    &lt;IconChevronRight size={12} /&gt;
  &lt;/Breadcrumbs.Separator&gt;
  &lt;Breadcrumbs.Item isCurrent&gt;Winter Collection&lt;/Breadcrumbs.Item&gt;
&lt;/Breadcrumbs&gt;</code></pre><ul><li><p><strong>Approach #2: Automated Separation</strong></p></li></ul><pre><code>&lt;Breadcrumbs 
  testId="auto-trail"
  separator={&lt;IconSlash size={10} /&gt;}
  items={[
    { href: '/', content: 'Home' },
    { href: '/settings', content: 'Settings' },
    { content: 'Profile' }
  ]}
/&gt;</code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Breadcrumbs</strong>: <code>items</code>, <code>separator</code></p></li><li><p><strong>Breadcrumbs.Item</strong>: <code>isCurrent</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Hooks: </strong><code>useBreadcrumbItems()</code></p></li><li><p><strong>Types: </strong><code>BreadcrumbItem</code>, <code>BreadcrumbSeparatorProps</code></p></li><li><p><strong>Constants</strong>: <code>BREADCRUMB_ARIA_LABEL</code>, <code>CURRENT_PAGE_ARIA_LABEL</code></p></li></ul></li></ul><h4>Review</h4><p>This output is really interesting because it felt <strong>Questionable</strong> at first but, after thinking over it a bit, I&#8217;ve realized that this <strong>Passable</strong> and actually slightly outperformed Claude 3.5 Sonnet.</p><p><strong>Approach #2</strong> is a bit worse than its Claude counterpart. I mean really, an <code>IconSlash</code> component? But this does at least give a sense of how things could be handled compositionally, and its easy enough to revise this part to <code>separator=&#8221;/&#8221;</code>.</p><p><strong>Approach #1 </strong>is a bit better than its Claude counterpart because <code>Breadcrumbs.Separator</code> is used correctly as a self-closing childless element.</p><div><hr></div><p><code>&lt;Table /&gt;</code>  -  &#9989; <strong>Passable Rough Draft<br></strong><em>DeepSeek R1</em></p><ul><li><p><strong>Pattern:</strong> Compound Component</p></li><li><p><strong>Subcomponents:</strong> <code>Table.Header</code>, <code>Table.Head</code>, <code>Table.Body</code>, <code>Table.Row</code>, <code>Table.Cell</code></p></li><li><p><strong>Approach #1: Basic Composition</strong></p></li></ul><pre><code>&lt;Table testId="user-table" fullWidth&gt;
  &lt;Table.Header&gt;
    &lt;Table.Head&gt;Name&lt;/Table.Head&gt;
    &lt;Table.Head align="end"&gt;Age&lt;/Table.Head&gt;
  &lt;/Table.Header&gt;
  
  &lt;Table.Body&gt;
    &lt;Table.Row&gt;
      &lt;Table.Cell&gt;John Doe&lt;/Table.Cell&gt;
      &lt;Table.Cell align="end"&gt;30&lt;/Table.Cell&gt;
    &lt;/Table.Row&gt;
    
    &lt;Table.Row disabled&gt;
      &lt;Table.Cell&gt;Jane Smith&lt;/Table.Cell&gt;
      &lt;Table.Cell align="end" loading&gt;{/* Shows skeleton */}&lt;/Table.Cell&gt;
    &lt;/Table.Row&gt;
  &lt;/Table.Body&gt;
&lt;/Table&gt;</code></pre><ul><li><p><strong>Approach #2: Enhanced Features</strong></p></li></ul><pre><code>&lt;Table testId="sortable-table" error={fetchError}&gt;
  &lt;Table.Header&gt;
    &lt;Table.Head 
      sortable 
      sortDirection="asc"
      onSort={handleSort}
    &gt;
      Name
    &lt;/Table.Head&gt;
    
    &lt;Table.Head 
      align="end"
      sortable
      sortDirection="desc"
      onSort={handleAgeSort}
    &gt;
      Age
    &lt;/Table.Head&gt;
  &lt;/Table.Header&gt;

  {!fetchError &amp;&amp; (
    &lt;Table.Body loading={isLoading}&gt;
      {/* Skeleton rows when loading */}
      {data.map((item) =&gt; (
        &lt;Table.Row key={item.id}&gt;
          &lt;Table.Cell&gt;{item.name}&lt;/Table.Cell&gt;
          &lt;Table.Cell align="end"&gt;{item.age}&lt;/Table.Cell&gt;
        &lt;/Table.Row&gt;
      ))}
    &lt;/Table.Body&gt;
  )}
&lt;/Table&gt;</code></pre><ul><li><p><strong>Discrete Props</strong>:</p><ul><li><p><strong>Table.Head</strong>: <code>align</code>, <code>sortable</code>, <code>sortDirection</code>, <code>onSort</code></p></li><li><p><strong>Table.Cell</strong>: <code>align</code>, <code>colSpan</code></p></li></ul></li><li><p><strong>Ancillary Resources:</strong></p><ul><li><p><strong>Events:</strong> <code>SortEvent</code>, <code>SortEventHandler</code></p></li><li><p><strong>Types: </strong><code>Alignment</code>, <code>SortDirection</code></p></li><li><p><strong>Constants:</strong> ALIGN_CLASSES, SORT_ICON_DIRECTION</p></li></ul></li></ul><h4>Review</h4><p>This output is wild! DeepSeek R1&#8217;s MoE reasoning shines through here. Though <strong>not as sophisticated</strong> as a code mockup for a <code>Table</code> component realistically would be, which may also touch on <strong>heterogenous levels of design responsibility</strong>, this output at least considers how our Table might impact the wider codebase as more is added to it.</p><p>And the code just <strong>feels cleaner</strong> than what we got out of ChatGPT o3 or Claude 3.5 Sonnet. That said, though, this output does have the same issues with custom events as Claude 3.5 Sonnet, so I&#8217;m pretty sure we need to be more explicit about expectations for custom events in the prompt.</p><div><hr></div><h2>Key Takeaways</h2><div><hr></div><p>This experiment showed that <strong>AI can accelerate UI code mockups</strong>, but human oversight is still essential. More importantly, I assumed that AI would get worse as components got more complex, but this <strong>did not</strong> turn out to be the case. In fact:</p><p>&#128313; <strong>DeepSeek R1 is best for more complex components</strong> &#8211; Claude 3.5 Sonnet and ChatGPT o3 both struggled proportionally as components grew in complexity.</p><p>&#128313; <strong>Claude 3.5 Sonnet is best for everything else</strong> &#8211; ChatGPT o3 lagged behind in generating substantive approaches, but Claude handled atomic and molecule-level components with ease.</p><p>AI isn&#8217;t ready to take on everything from planning to execution, but it <strong>can speed up ideation and exploration</strong>. The future of <strong>AI-assisted UI development</strong> isn&#8217;t just about velocity&#8212;it&#8217;s about leveraging AI to achieve new professional levels of craftsmanship without hitting any speed bumps.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/ai-driven-code-mockups?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/ai-driven-code-mockups?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>If you enjoyed this post, please subscribe, hit the &#10084;&#65039; button, and share/restack &#128257; it with others who might find it helpful!</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How To Write A Code Mockup]]></title><description><![CDATA[What if we delivered Design System components the same way Designers create the very UI we're building?]]></description><link>https://blog.robhameetman.com/p/how-to-write-a-code-mockup</link><guid isPermaLink="false">https://blog.robhameetman.com/p/how-to-write-a-code-mockup</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Thu, 23 Jan 2025 11:33:32 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e525a0d0-6699-4e01-9dad-7c3a58d94570_2048x2048.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SmUJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SmUJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 424w, https://substackcdn.com/image/fetch/$s_!SmUJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 848w, https://substackcdn.com/image/fetch/$s_!SmUJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 1272w, https://substackcdn.com/image/fetch/$s_!SmUJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SmUJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp" width="1456" height="1456" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1456,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1980222,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SmUJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 424w, https://substackcdn.com/image/fetch/$s_!SmUJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 848w, https://substackcdn.com/image/fetch/$s_!SmUJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 1272w, https://substackcdn.com/image/fetch/$s_!SmUJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96d1fd6c-955a-4be9-abba-a48a0b89aeb1_2048x2048.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Engineers building great design systems mockup their most impactful and valuable abstractions. They play with critical building blocks and ensure reliability before sharing them with others. And they look at how the code will be used from the perspective of an Engineer without any historical context, making adjustments along the way which create more opportunities for others to make correct assumptions.</p><p>Below is the procedural and mental framework I rolled out for doing just this when adding components to our design system at VividSeats. Our business goals require high delivery velocity for components inner-sourced by contributors across teams as part of ongoing feature work without sacrificing quality or flexibility in branding and themeability, for which our requirements and standards are about as advanced as it gets.</p><p>This process, combined with a healthy dose of scaffolding and automation, is designed to empower any Frontend Engineer at VividSeats to successfully build a design system component to our elevated standards, even without the pre-requisite specialized expertise that comes with a background in design or any focus on foundational work or design systems in previous roles.</p><div><hr></div><p><em>&#128075; Hey, it&#8217;s <a href="https://www.linkedin.com/in/rhameetman/">Rob</a>. Welcome to UI Engineering Excellence. If you enjoy this post, please hit the &#10084;&#65039; button and share/restack &#128257; it with others who might find it helpful!</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/how-to-write-a-code-mockup?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/how-to-write-a-code-mockup?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h2>Code Mockups For Enterprise-Strength Design System Components</h2><div><hr></div><blockquote><p>&#8505;&#65039; A <strong>code mockup</strong> is both a process and a deliverable. We start by diving deep into requirements outlined in design mockups mapping out multiple potential paths to full viability. From there, we refine and converge on a single pattern, charting a roadmap for how it can evolve as the component grows in complexity. Along the way, we adapt and adjust, ensuring the solution aligns seamlessly with our business goals.</p></blockquote><div><hr></div><h3>1. Identify Requirements &amp; Constraints</h3><ol><li><p><strong>Assess The Design Mockup (assuming we have one)</strong><br>Design mockups aren&#8217;t always a given, but when they are available, they serve as the <strong>source of truth</strong> throughout the development process. Once the component is delivered and live in production, the implementation itself takes over as the definitive reference point.</p></li><li><p><strong>Identify Design Discrepancies &amp; Constraints</strong><br>What design decisions&#8212;such as sizing, spacing, positioning, or alignment&#8212;change across different states? Are there clear boundaries or implicit constraints on how these differences can vary? Which aspects of the component need to be themeable, and which parts of the design remain stable ?</p></li></ol><div><hr></div><blockquote><p><strong>&#127919; Goal</strong>: Gain just enough understanding to confidently start mocking up code.</p></blockquote><div><hr></div><h3>2. Choose A Component Pattern</h3><p>Our UI composition leans heavily on 3 core patterns:</p><p><strong>Standalone Components</strong><br>The component functions as a self-contained unit, with its complexity scaling based on the number and type of props&#8212;think Checkbox, Text, etc.<br><em>Example:</em></p><pre><code><code>&lt;Checkbox id="1" checked={checkAll}&gt;{label1}&lt;/Checkbox&gt;
&lt;Checkbox id="2" checked={checkAll}&gt;{label2}&lt;/Checkbox&gt;</code></code></pre><div><hr></div><p><strong>Compound Components<br></strong>The component is multifaceted with specialized, often optional, subcomponents that are not reusable elsewhere.<br><em>Example:</em></p><pre><code><code>&lt;Checkbox.Group checked={checkAll}&gt;
 &lt;Checkbox&gt;{label1}&lt;/Checkbox&gt;
 &lt;Checkbox&gt;{label2}&lt;/Checkbox&gt;
&lt;/Checkbox.Group&gt;</code></code></pre><div><hr></div><p><strong>Base Components<br></strong>The component is reduced to essential presentation logic common across all variants and then used as the root node in explicit components for specialized variants.<br><em>Example:</em></p><pre><code><code>&lt;SuccessCheckbox /&gt;
&lt;ErrorCheckbox /&gt;
&lt;InfoCheckbox /&gt;
&lt;WarningCheckbox /&gt;</code></code></pre><div><hr></div><blockquote><p><strong>&#129504; K</strong><em><strong>eep In Mind: </strong>Most <a href="https://atomicdesign.bradfrost.com/chapter-2/">organism-level components, as well as template/page/view-level components</a> in the Design System, become <strong>Compound Components</strong>. These typically serve as base components for variants that live outside the Design System. On rare occasions, a compound component may also act as a base for variant components that remain within the Design System. When this happens, we refer to the pattern as a <strong>Compound-as-Base Hybrid.</strong></em></p></blockquote><div><hr></div><h3>3. Mock Up Multiple Approaches</h3><p>Just as Designers draft and explore multiple solutions, Engineers contributing to the Design System should draft at least two or three usage mockups for the chosen pattern. This allows us to evaluate how seamlessly Engineers focused on feature work can leverage what we&#8217;re building to address common design and UX challenges. Key areas we often explore include:</p><ol><li><p><strong>Pattern Adequacy</strong><br>How many patterns should we evaluate for this component? More complex components are often compound components, while simpler, atomic components usually offer more flexibility. For example, <code>&lt;Button /&gt;</code> and <code>&lt;Icon /&gt;</code> are commonly used as base components inside and outside the Design System. Should we allow their variants to emerge organically as the system evolves, or explicitly define them upfront? If explicit, which variants make the most sense to include?</p></li><li><p><strong>Design Responsibility</strong><br>How many assumptions do components, subcomponents, and variants make about styling their content? To what extent are subcomponents or variants allowed to be aware of or depend on the specifics of their content?</p></li><li><p><strong>Structural Flexibility</strong><br>How easily should we be able to rearrange or conditionally render parts of the component? What is the visual or spatial &#8220;scope&#8221; in these cases&#8212;specific content, entire sections, full tabs? Do the variants need to be reusable enough to justify creating discrete components for each?</p></li><li><p><strong>Prop Composition</strong><br>How does this component's contract influence the broader system? Does it introduce any new standardized props? If we adjust the type signature of existing standardized props to enhance shared functionality across components, will we need to revisit and update other components using the same prop to maintain the library&#8217;s integrity?</p></li></ol><div><hr></div><blockquote><p><strong>&#9757;&#65039; Tip:</strong> Keep your mockups lean and to-the-point. Prioritize usage examples and focus on how the code &#8220;feels&#8221; to read and write when achieving the desired state.</p></blockquote><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><em>&#10084;&#65039; Like what you&#8217;re seeing so far?</em></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h3>4. (Optional) Mock Up Props</h3><ol><li><p><strong>List Standardized Props</strong><br>Which props from other components are reusable in this component?</p></li><li><p><strong>List Discrete Props</strong><br>Identify the props unique to this specific component. Which of these are required, and which are optional? What are their default values? Can we include any pass-through props without exposing underlying implementation details? If using a base component pattern, determine which base component props are abstracted by and omitted from the variant components.</p></li><li><p><strong>Limit Style/Design Props</strong><br>Are we helping Engineers focused on feature work make accurate assumptions? For example, if the component requires flexible width, should we provide a <code>width</code> prop for explicit, potentially complex values, or opt for a simpler <code>fullWidth</code> prop to streamline usage?</p></li><li><p><strong>Explore Multiple Techniques</strong><br>When design decisions like alignment, size, and roundedness are tightly coupled, should we simplify with a single prop or maintain flexibility by using granular props for each decision? What are the trade-offs, and how does each approach look in practice?</p></li><li><p><strong>Customize Discrete Events</strong><br>Can we make event handling more granular and reusable for common scenarios? For example, instead of accepting a generic <code>onKeyDown()</code> handler to augment functionality when the &#8220;Tab&#8221; key is pressed, could we introduce an <code>onPressTab()</code> prop that leverages a custom <code>TabEventHandler</code> to handle more specific custom <code>TabEvents</code>? <em>(Hint: You best believe we can!)</em></p></li><li><p><strong>Standardize Type Signatures</strong><br>Do we need to introduce new utility hooks or types to support newly standardized props, such as custom event handlers?</p></li></ol><div><hr></div><blockquote><p><strong>&#127919; Goal:</strong> Make it easy for any consumer (including your future self) to intuitively understand how to use this component and confidently make accurate assumptions, even when facing gaps in technical expertise or domain knowledge.</p></blockquote><div><hr></div><h3>5. (Optional) Mock Up Reusable Utils</h3><ol><li><p><strong>Identify or Create Helper Hooks, Utilities, or Types</strong><br>e.g. <code>useVerticalAlignment()</code>, <code>useQueryParamState()</code>, <code>EscapeEvent/EscapeEventHandler/useEscapeEvents()</code>, or defining notable constants and enums.</p></li><li><p><strong>Centralize Reusable Utils<br></strong>Consolidate logic or styling transformations to streamline the process of building Design System components and reduce duplication.</p></li><li><p><strong>Explore Multiple Options (As Needed)</strong></p><p>For particularly large or complex enums, would breaking them into smaller pieces improve usability? How should functional hooks, like those for filtering and sorting, be composed to handle overlapping or chained behaviors effectively? Consider the trade-offs and implementation details for these approaches.</p></li></ol><div><hr></div><blockquote><p><strong>&#127919; Goal</strong>: Keep the code DRY and self-documenting while avoiding duplicated logic in each component.</p></blockquote><div><hr></div><h3>6. (Required) Review With Maintainers</h3><ol><li><p><strong>Share Final Mockup / Chosen Approach</strong></p><p>Present usage examples to the Design System group. If the work will be carried out by another Engineer, ensure they&#8217;re involved in the review process.</p></li><li><p><strong>Gather Feedback</strong></p><p>Verify that the proposed implementation adheres to Design System guidelines, naming conventions, WCAG compliance, and other key standards.</p></li><li><p><strong>Discuss Future Use</strong></p><p>Consider whether the pattern or component could be valuable for other teams or projects. Explore how distribution logic might evolve as the business&#8217;s brand composition changes.</p></li><li><p><strong>Achieve Consensus</strong></p><p>Secure approval from the Design System maintainers and move forward with development.</p></li></ol><div><hr></div><blockquote><p><strong>&#127919; Goal</strong>: Validate the approach and maintain consistency with system-wide standards.</p></blockquote><div><hr></div><h3>7. Make Adjustments</h3><ol><li><p><strong>Tweak the Pattern(s)</strong></p><p>Make minor adjustments as unforeseen challenges arise.</p></li><li><p><strong>Strategically Plan Rewrites</strong></p><p>Identify when a shift from one pattern to another might be necessary. If the component becomes overly complex, should it be broken into smaller pieces or rewritten using a more sophisticated pattern?</p></li><li><p><strong>Add Snippets in Storybook</strong></p><p>Create a Storybook story for each design/state variation from the design mockup. Ensure the resulting code snippets precisely match the code mockup for each variant.</p></li></ol><div><hr></div><blockquote><p><strong>&#127919; Goal</strong>: Deliver a polished, thoughtfully refined implementation, ready for actual build or integration.</p></blockquote><div><hr></div><h3>Summary</h3><ol><li><p><strong>Identify Requirements &amp; Constraints</strong></p></li><li><p><strong>Choose a Component Pattern</strong></p></li><li><p><strong>Mock Up Multiple Approaches</strong></p></li><li><p><strong>(Optional) Mock Up Props</strong></p></li><li><p><strong>(Optional) Mock Up Reusable Utils</strong></p></li><li><p><strong>(Required) Review With Maintainers</strong></p></li><li><p><strong>Make Adjustments</strong></p></li></ol><div><hr></div><p><em>&#128075; <a href="https://www.linkedin.com/in/rhameetman/">Connect with me on LinkedIn.</a></em></p><p><em>PS: If you&#8217;re enjoying UI Engineering Excellence, hitting the &#10084;&#65039; button and sharing/restacking &#128257; goes a long way in helping me grow the publication. Please take a moment to help spread the word. Thank you!</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/p/how-to-write-a-code-mockup?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/p/how-to-write-a-code-mockup?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LRLx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png" width="300" height="162.12121212121212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:428,&quot;width&quot;:792,&quot;resizeWidth&quot;:300,&quot;bytes&quot;:37166,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/154924704?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LRLx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 424w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 848w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1272w, https://substackcdn.com/image/fetch/$s_!LRLx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17b509d7-f748-4f7e-92db-a8ed3d1f3352_792x428.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p>]]></content:encoded></item><item><title><![CDATA[[TEMPLATE] New Post]]></title><description><![CDATA[A template for faster outlines and consistent content strategy.]]></description><link>https://blog.robhameetman.com/p/template-post</link><guid isPermaLink="false">https://blog.robhameetman.com/p/template-post</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Wed, 01 Jan 2025 06:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!YI39!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of UI Engineering Excellence. I write for Frontend Engineers and Product Developers on how to be the best at building apps that win in the market. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YI39!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YI39!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!YI39!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!YI39!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!YI39!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YI39!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:93196,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/171842138?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YI39!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 424w, https://substackcdn.com/image/fetch/$s_!YI39!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 848w, https://substackcdn.com/image/fetch/$s_!YI39!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 1272w, https://substackcdn.com/image/fetch/$s_!YI39!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a0b662a-fade-413d-b0ca-7615df49aec0_2464x1856.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Edit blog cover and Save As WebP (Lossless) with EXIF data and upload. Make s&#8230;</figcaption></figure></div>
      <p>
          <a href="https://blog.robhameetman.com/p/template-post">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[[TEMPLATE] Recap: [BLOG] in [YEAR]]]></title><description><![CDATA[The year&#8217;s most-read articles, some personal favorites, and a look back at a busy year in the design tech]]></description><link>https://blog.robhameetman.com/p/template-recap-blog-in-year</link><guid isPermaLink="false">https://blog.robhameetman.com/p/template-recap-blog-in-year</guid><dc:creator><![CDATA[Rob Hameetman]]></dc:creator><pubDate>Wed, 01 Jan 2025 06:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gT76!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Hi, I&#8217;m Rob. This is a free issue of [BLOG]. I write for [AUDIENCE] on how to [GOAL TO ACHIEVE]. Subscribe if this is useful. Paid readers get early looks and occasional behind&#8209;the&#8209;scenes notes:</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://blog.robhameetman.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://blog.robhameetman.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gT76!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gT76!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 424w, https://substackcdn.com/image/fetch/$s_!gT76!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 848w, https://substackcdn.com/image/fetch/$s_!gT76!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 1272w, https://substackcdn.com/image/fetch/$s_!gT76!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gT76!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:895966,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://blog.robhameetman.com/i/171158859?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gT76!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 424w, https://substackcdn.com/image/fetch/$s_!gT76!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 848w, https://substackcdn.com/image/fetch/$s_!gT76!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 1272w, https://substackcdn.com/image/fetch/$s_!gT76!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab53780c-e9a1-4c60-aa0a-7ec9819d167b_1600x1205.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Edit blog/covers/annual.psd to change the BLOG and update the year. Save as WebP (Lossless) with EXIF data and upload. Delete this caption if necessary.</figcaption></figure></div><p>As [YEAR]&#8230;</p>
      <p>
          <a href="https://blog.robhameetman.com/p/template-recap-blog-in-year">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>