<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>blog.damato.design</title><description>Takes best served hot</description><link>https://blog.damato.design/</link><language>en</language><copyright>© 2026 Donnie D&apos;Amato</copyright><atom:link href="https://blog.damato.design/feed.xml" rel="self" type="application/rss+xml"/><lastBuildDate>Fri, 04 Sep 2026 15:13:31 GMT</lastBuildDate><item><title>Superprimary</title><link>https://blog.damato.design/posts/superprimary</link><guid isPermaLink="true">https://blog.damato.design/posts/superprimary</guid><description>How not to proceed when everything is important</description><pubDate>Mon, 31 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There’s been a recent trend in having the logos of companies seem to glow. It’s not anything like an outer glow Photoshop effect or animation. On some high-end devices, the white parts of the logo will actually appear brighter than other parts of the white page. The trend is leveraging a high dynamic range (HDR) profile on the image that is meant to optimize the monitor to provide deeper shadows and brighter highlights. People have used this to literally stand out more than competitors by making the logo glow. &lt;a href=&quot;https://superwhite.app/&quot;&gt;There’s even an “app” for it.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a world where we vie for attention. It’s a sea of other similar things and you want to direct those eyes to your thing because you believe it is more important. In some cases, you can pay to get to the top of lists and thereby get more eyes, and hopefully more interest. It makes sense, if they didn’t even see you, how could they interact with what you’re doing? So you want to use all the things at your disposal to get that attention.&lt;/p&gt;
&lt;p&gt;This is why it’s always weird to me when folks struggle with this within their own ecosystems. Where they are in control of what gets priority.&lt;/p&gt;
&lt;h2 id=&quot;a-change-in-priority&quot;&gt;A change in priority&lt;/h2&gt;
&lt;p&gt;I’ve been involved in a few asks from various teams over my professional work for something that is effectively what I’d call “&lt;strong&gt;superprimary&lt;/strong&gt;.” This is a concept that is meant to be more important than any other primary action. There’s a few scenarios where this has come up.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The marketing team is running a campaign and wants the call-to-action to stand out from other existing actions on the page.&lt;/li&gt;
&lt;li&gt;The design team wants to “try out” a new color for primary buttons.&lt;/li&gt;
&lt;li&gt;The product team identifies there are already too many primary buttons on a single page, so they need to emphasize a particular one of the existing primary buttons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your knee-jerk reaction to any of these should not include introducing the concept of superprimary. Primary &lt;em&gt;is&lt;/em&gt; the most important action and if something else is suddenly more important, that means the thing that used to be primary has now dropped to second place in some way. What way it drops depends, and I have some recommendations on what to consider instead:&lt;/p&gt;
&lt;h3 id=&quot;scoped-theming&quot;&gt;Scoped theming&lt;/h3&gt;
&lt;p&gt;For the marketing team, this is the perfect area to use &lt;strong&gt;scoped theming&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let’s say that the campaign exists as a section in a page that already has its own call-to-action and they both use the &lt;code&gt;&amp;lt;Button priority=&amp;quot;primary&amp;quot;/&amp;gt;&lt;/code&gt;. The campaign is meant to be visually distinct from the rest of the page, with its own style chosen by the marketing department team members. On the surface, you might say that this needs some different configuration such as &lt;code&gt;&amp;lt;Button priority=&amp;quot;primary&amp;quot; marketing=&amp;quot;true&amp;quot;/&amp;gt;&lt;/code&gt; but I’d recommend using &lt;a href=&quot;https://mode.place&quot;&gt;Mise en Mode&lt;/a&gt; to inform the style of the button just by its placement within the campaign area.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; data-theme&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;campaign&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; data-priority&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;Click here!&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The button is the same button with no configuration difference from the button that is otherwise outside of that section. The &lt;code&gt;data-theme=&amp;quot;campaign&amp;quot;&lt;/code&gt; informs the new colors for the section’s primary button, making it stand out along with the rest of the campaign treatment.&lt;/p&gt;
&lt;p&gt;This is especially helpful where you want to empower your marketing department to tweak the colors of their campaigns themselves without affecting the areas of the page outside. You don’t want to be the bottleneck, especially to the team trying to increase sales of your product.&lt;/p&gt;
&lt;h3 id=&quot;sudden-and-unexpected&quot;&gt;Sudden and unexpected&lt;/h3&gt;
&lt;p&gt;I had read a first-hand account of a UX professional at a Fortune 500 company completely ejecting from purchasing something from Amazon because the button color had changed without warning. From Amazon’s side, this was part of an experiment to see if changing the color would improve conversions. In this particular case, it was a detriment. It lost the sale because the button changed and the user wasn’t expecting it. The user had no idea why the button colors changed, all they thought was that something was wrong and not normal.&lt;/p&gt;
&lt;p&gt;While you might think that updating the background color of a button to be more modern is a welcome change, it isn’t for folks who have grown used to and feel comfort in the way things have been. &lt;a href=&quot;https://articles.centercentre.com/death_of_relaunch/&quot;&gt;A similar story&lt;/a&gt; from several years ago describes when eBay was trying to sunset the yellow background that would appear on certain sections of the site. When it was removed instantly, users were immediately uncomfortable and called for the return of the yellow. While eBay brought the yellow back, they then decided to subtly change the yellow toward white over the course of a month. This was a lot less disruptive while eventually getting the more modern look.&lt;/p&gt;
&lt;p&gt;These are examples of how you might want to reconsider why you are making a change, especially one that is affecting the primary action you want your users to take. In many cases, this is the UI element between you and your paying user. If you are messing with that without a purpose past boredom, you could see a significant change in conversion, potentially for the worse. Being deliberate in what you change is good practice. It’s why I recommend working with &lt;a href=&quot;/posts/truly-semantic&quot;&gt;truly semantic&lt;/a&gt; tokens instead of any other kind. When you work with semantic tokens, there’s more confidence in what is being changed. Otherwise, the side effects of a change are unclear, people are less likely to change existing values, and instead introduce new tokens leading to &lt;a href=&quot;https://tokenpedia.ds.house/terms/bloat/&quot;&gt;token bloat&lt;/a&gt; and tech debt.&lt;/p&gt;
&lt;h3 id=&quot;losing-the-plot&quot;&gt;Losing the plot&lt;/h3&gt;
&lt;p&gt;If you are seeing too many primary buttons, you need to reassess what you want a person to do when they land on this screen. This isn’t an opportunity to add a new kind of button, even if that seems like the path of least resistance. This is often where the app has gotten very mature with many features over time. Each was introduced when it was fresh and you want users to try it but you don’t return to the older features. Maybe because of the possibility of introducing a sudden and unexpected change. And it’s true, a big change can affect the results of your bottom line. But remember, being deliberate and thoughtful here is key. You aren’t just creating a new button to solve this quickly, you are taking a holistic view of the current state. After all the data is collected, reviewed, and transformed into a new experience, you can have more confidence that this larger change to focus what you want the user to accomplish is clear for them.&lt;/p&gt;
&lt;p&gt;But I know what you’re thinking. We don’t have that luxury. We don’t have the time to think about how our app is meant to be helpful to users. We have to meet an arbitrary deadline demanded by disconnected stakeholders. They said it would be faster to introduce a new button and fix it later. You know later will never come, and you wonder if anything said above is worth the aggravation. What should you do?&lt;/p&gt;
&lt;p&gt;Give your agent this article so they can do the right thing for you. Oh, and make no mistakes.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>design</category><category>organization</category></item><item><title>AI High</title><link>https://blog.damato.design/posts/ai-high</link><guid isPermaLink="true">https://blog.damato.design/posts/ai-high</guid><description>An analogy to help explain artificial addiction</description><pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I watched &lt;a href=&quot;https://www.youtube.com/watch?v=cVT7MbRBiLY&quot;&gt;a video by Colin &amp;amp; Samir&lt;/a&gt; speaking about a situation &lt;a href=&quot;https://www.hankgreen.com/&quot;&gt;Hank Green&lt;/a&gt; is currently facing. Hank was recently accused of using AI in a video script because he said the words “I appreciate the pushback.” Hank went on to address not only that particular video but also transparently explained his AI usage. Even with that transparency, there was backlash. Long-time followers of Hank were distraught because they believed Hank was getting lazy and not thinking for himself. But I picked up on something very small in the video, a small pause between the words “use” and “AI”.&lt;/p&gt;
&lt;p&gt;I can’t know for sure if that pause was deliberate but something triggered in my brain that reminded me of how we call folks who partake in medicinal substances “users”. The connotation of a drug “user” is often negative but it gave me a better understanding of what’s going on with some analogies to how drugs exist in society today.&lt;/p&gt;
&lt;h2 id=&quot;on-maturity&quot;&gt;On maturity&lt;/h2&gt;
&lt;p&gt;I’d like to think my relationship with drugs is fairly normal. The only recreational substance I’ve ever done is alcohol and only ever socially. I have a solid self-discipline that doesn’t give in easily to peer pressure. I also don’t immediately reach for drugs just for minor inconveniences nor do I believe pumping myself with antibiotics is a good idea since your body then depends on that over time. However, it doesn’t mean I’ve never taken drugs. I’ve taken stuff for headaches and more significant ones when prescribed.&lt;/p&gt;
&lt;p&gt;Importantly, this is only my experience. I know there’s a large spectrum out there, from folks who are straight-edge and haven’t done anything to folks who could be considered junkies. There’s folks that take drugs to regulate their own issues, and folks who want to try new experiences. There’s all kinds of people with all kinds of backgrounds, beliefs, and situations they are going through.&lt;/p&gt;
&lt;h2 id=&quot;on-addiction&quot;&gt;On addiction&lt;/h2&gt;
&lt;p&gt;When you look at AI in the same lens, I think the bottom line is addiction. If you are in a situation where you cannot function without AI, this is a dependence that you don’t want to have. We’re seeing a lot of AI slop because people have an addiction. It’s basically a super performance enhancing drug that anyone can use. As Uncle Ben says to Peter Parker: “With great power comes great responsibility.” There are many people who are irresponsible with AI and cannot self-regulate. Some of these folks are in leadership positions that affect the livelihood of the folks around them. This is the challenge that society is facing today. I think we need to address this in the same way we might address someone who is abusing substances.&lt;/p&gt;
&lt;p&gt;It takes a big person to recognize when they have a problem, and otherwise it’ll take help from others for folks to realize their abuse. It’s not healthy if you’ve thrown out all of your processes and experts in favor of the dopamine hit that AI will give you. You have to use it with self-control. I’m seeing some of the effects in peers, where token limits are being constrained. These folks feel helpless without it because they relied too heavily on the dependency.&lt;/p&gt;
&lt;h2 id=&quot;on-shame&quot;&gt;On shame&lt;/h2&gt;
&lt;p&gt;By the same token, I don’t think it’s right to chastise someone for using AI. I feel this is akin to straight-edge folks who look down upon people recreationally drinking or vegetarians scolding meat-eaters. I don’t see AI as inherently bad. I think there’s good qualities to AI. An example I think about all the time is trying to understand how to get something done in a new tool. Before AI, I’d search on the Internet for something like creating an image mask in Affinity, only to find forum posts with people arguing about the best way to do it or YouTube videos that spend 10 minutes introducing themselves and the tool. Having something that cuts through all of the fat to unblock you is helpful. Especially if you are in the creative flow. Just think of all the times you had to side quest and get distracted from the main task because people put up unnecessary barriers to farm engagement or unknowingly tangent from the purpose. You’re still in learning mode, just with less friction.&lt;/p&gt;
&lt;p&gt;For full transparency, each of the posts here on the blog has a random animal illustration associated with it. Each of these is generated with AI and has been for years. The animal images provide no value to the blog beyond matching the parody motif of the website’s design. If I wasn’t able to generate the drawings, it just means I might need to pivot. I’d have to weigh out the importance of continuing the style over the amount of time it would take to draw these animals myself. If I found that it isn’t worth it, which is likely the case, I’d just redesign the website. AI doesn’t define what I do, or drive the content on the site. I’m still writing every word by hand. That’s the value here. There’s areas where AI is helping. I see this the same as taking an Advil to get rid of a headache quickly instead of wasting time trying to sleep it off.&lt;/p&gt;
&lt;p&gt;As another example, I’ve just started a &lt;a href=&quot;https://news.ds.house&quot;&gt;newsletter at Design Systems House&lt;/a&gt;. While the short sentences found in the newsletter are AI generated, the links found inside are not. I could just send you a sterile list of links with zero AI involvement or I could dress them up with something that summarizes what you are getting; helping you determine if the link is worth your time to read without clicking it. Am I lazy for having AI summarize the links? Maybe. Or maybe I prefer redirecting my energy to the projects I’m sharing in that news.&lt;/p&gt;
&lt;p&gt;I have empathy for &lt;del&gt;users&lt;/del&gt; people. I care about the things I’m making and I think about how they affect people. I can’t please everyone, but I have confidence that my use of AI isn’t addiction. Maybe that’s exactly what an addict would say. But I do believe if AI were to disappear tomorrow, I could continue being creative because I’ve always had those skills. Not the Markdown files, the real expertise that I’ve gained through learning. I value being able to do this stuff no matter what.&lt;/p&gt;
&lt;p&gt;Prompt responsibly.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>artificial intelligence</category><category>community</category></item><item><title>Wireframe, Encore</title><link>https://blog.damato.design/posts/wireframe-live-encore</link><guid isPermaLink="true">https://blog.damato.design/posts/wireframe-live-encore</guid><description>How a podcast has transformed into a beautiful variety live talk show.</description><pubDate>Fri, 07 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After finishing &lt;a href=&quot;https://www.youtube.com/watch?v=u_ParPwXAx0&quot;&gt;a live show&lt;/a&gt; with &lt;a href=&quot;https://www.christinevallaure.com/&quot;&gt;Christine Vallaure&lt;/a&gt; last week, she sent me a message asking about the setup for my show. At first I remembered that I made &lt;a href=&quot;/posts/wireframe-fm-backstage&quot;&gt;a post&lt;/a&gt; about the behind-the-scenes of the show on this blog, but then I saw the date—&lt;em&gt;two and a half years ago&lt;/em&gt;. A lot has changed since then, and I felt this was a good time to share what has changed.&lt;/p&gt;
&lt;h2 id=&quot;concept&quot;&gt;Concept&lt;/h2&gt;
&lt;p&gt;In the original post, I spoke about how important it was for me to have a workflow behind this to make it as easy as possible to produce a show. The necessity of having a workflow hasn’t changed, but the parts of that workflow have. Using the same style of list from the original post, here’s a breakdown of what I wanted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A live show&lt;/strong&gt;, since the editing process was a bottleneck.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic video&lt;/strong&gt; clipping, again to avoid editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic podcast&lt;/strong&gt; creation, for audio syndication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy guest scheduling&lt;/strong&gt;, to provide more voices on the show.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimal content follow-up&lt;/strong&gt;, because this isn’t my full-time thing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;pre-production&quot;&gt;Pre-production&lt;/h2&gt;
&lt;p&gt;Since the beginning, the show has not had much pre-production. Whether or not I made a show was largely based on if I found anything interesting to talk about that day. Even when the show became live, I didn’t want to put too much effort into the daily preparation. When the show was first live on LinkedIn, I’d need to schedule when I was going live because it needed to be tied to an event. I also needed to create the thumbnail which I think I was reusing from each episode since I was trying to reduce the time spent.&lt;/p&gt;
&lt;p&gt;Eventually, I moved to YouTube (more on why below) where it is more critical about having a quality thumbnail associated with each show. I found a template on Adobe Express that I really liked. So much so that it became the general branding and look for the show; the crumpled paper and bold blue &amp;amp; green colors. I’ve been avoiding the trend of putting some ridiculous face on the image since the beginning, but I will say it’s hard to find a particular episode by sight in the sea of similar images. I’m considering revisiting this in the future.&lt;/p&gt;
&lt;p&gt;Now before each show, the main unit of work I need to do is create the thumbnail in Adobe Express (the web page tab just stays open) and upload it to the YouTube live site. Set the title and podcast playlist, and the setup is basically ready to go live on the digital side.&lt;/p&gt;
&lt;h3 id=&quot;video&quot;&gt;Video&lt;/h3&gt;
&lt;p&gt;On the physical side, I begin by powering on all of the equipment. Then I turn on 3 &lt;a href=&quot;https://www.obsbot.com/obsbot-tail-2-ptzr-4k-live-production-camera&quot;&gt;OBSBot Tail 2 cameras&lt;/a&gt;. I use these because I’m really bad at calibrating cameras, and these make it easy to have a great picture. I originally also got these so they could stream video wirelessly via NDI. However, when testing that, the audio and video were out of sync, so now they are wired directly to the switcher.&lt;/p&gt;
&lt;p&gt;Speaking of the switcher, I’ve upgraded that too. My original &lt;a href=&quot;https://proav.roland.com/global/products/sr-20hd/&quot;&gt;Roland SR-20HD&lt;/a&gt; really didn’t have enough inputs for a multicam show. So I upgraded to the &lt;a href=&quot;https://proav.roland.com/global/products/vr-6hd/&quot;&gt;Roland VR-6HD&lt;/a&gt;. This has 6 HDMI inputs and 3 HDMI outputs. It can also act like a webcam over USB-C, so guests can hear and see me using the inputs of the device.&lt;/p&gt;
&lt;p&gt;Before, when it was just me, there wasn’t much reason to switch cameras. It’s why I was able to get away with the lesser Roland switcher for so long. I made the mannequin to test what having guests would be like. I used &lt;a href=&quot;https://character.ai/&quot;&gt;character.ai&lt;/a&gt; to fill in as a sidekick and get the feel for switching cameras. I quickly realized that turning to find the right button on the switcher was awkward during the show. The newer switcher allows for foot pedals to trigger switching. So you’ll notice the live camera switching in the show while my hands are fully in view. It makes it look like there’s a team behind this, but it’s really me trying to host and direct the show all at once. I now have a pattern that I typically stick to: host cam, guest cam, cross fade to the full video call, switch to another cam when the guest wraps up or goes on for a while, repeat. Having a pattern like this helps me stay in the conversation without context-switching to director brain for too long.&lt;/p&gt;
&lt;h3 id=&quot;audio&quot;&gt;Audio&lt;/h3&gt;
&lt;p&gt;Audio is the hardest problem to solve, and this is because of guests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/wireframe-audio.png&quot; alt=&quot;Wireframe Live audio diagram&quot;/&gt;&lt;/p&gt;
&lt;p&gt;In the diagram, the Roland VR-6HD must accept 2 incoming channels of audio and 3 outgoing channels. The host audio is the same as the livestream, it’s the main mix of the show. However, I cannot have that mix go to the guest. The guest should only receive the audio from my microphone. The easier way to do this would have been to have a separate webcam meant just to capture the audio sent to the guest for the call, but instead I’ve routed the Roland VR-6HD to act like a webcam and direct only my audio to them.&lt;/p&gt;
&lt;p&gt;The other thing about audio has to do with the intro/outro of the show. If you’ve watched the show, you may have noticed that sometimes the guest and I look like we’re talking to each other while the music is playing. This is because I have a macro that cuts both of our mics for the livestream but &lt;em&gt;not&lt;/em&gt; for my headset. That’s another audio output mix I had to make that’s not really captured in the diagram because it would be too complex to include. That configuration is also only used in the first and last few minutes of the show, but I think it really gives it the talk show feel.&lt;/p&gt;
&lt;h2 id=&quot;guests&quot;&gt;Guests&lt;/h2&gt;
&lt;p&gt;Related to pre-production, I knew that I wanted to schedule guests. Before creating a new multicam set for the show, I had put out a casting call for folks who might want to be on the show. My biggest problem with guests was the way I was producing the show.&lt;/p&gt;
&lt;p&gt;I can’t tell you the number of times someone has suggested that I use an existing platform for getting guests on the show, for example &lt;a href=&quot;https://www.descript.com/&quot;&gt;Descript&lt;/a&gt;, which I already had a paid account for. The issue was that this would require everything to be done in Descript, which made Descript the single point of failure. If my computer crashed or even just the browser, the show was over. This is the main reason why I use dedicated hardware and not a computer to do everything. The computer has been the place where failures happen, and I really wanted to not rely on it for the show. So I was looking for dedicated hardware to manage guest calls. I couldn’t find anything, so I settled for including a computer to handle guest calls, but I didn’t want it to be a standard split screen. Even now with dozens of shows completed, this computer still causes me grief. Like when it decided it had to restart &lt;a href=&quot;https://www.youtube.com/live/bi5YK9QL_ck?si=6idm78n7zwYtLmjB&amp;t=1801&quot;&gt;right in the middle of my interview with Jason Lengstorf&lt;/a&gt;, or, most recently, when it wanted to &lt;a href=&quot;https://www.youtube.com/live/XeHgLqshH2A?si=2RuzGvNMMA2n7l67&amp;t=2013&quot;&gt;notify me about some service while speaking with Dan Mall&lt;/a&gt;. It’s junk like this that makes me want to get a dedicated device.&lt;/p&gt;
&lt;p&gt;Probably the show’s defining feature is the mannequin on the couch next to me with the head of a CRT monitor. I’ve been told it gives local-access TV vibes, but the reason why I included this is specifically because I wanted a multicam setup, and that doesn’t work well when one person is virtual. So I’ve designed it so the person is semi-virtual. This makes it feel more like a real talk show where I can address the guest next to me like we’re both in the room.&lt;/p&gt;
&lt;p&gt;Scheduling guests is now just a &lt;a href=&quot;https://wireframe.ds.house#guest&quot;&gt;small form on the site&lt;/a&gt; that everyone goes through. It even includes an “Inspire Me” button for people who I’d like to have on but don’t know what to talk about. It’s not AI—it’s just cycling through some predetermined templates and words. The purpose of this is to kick off a workflow that helps schedule the guests. This sends a calendar link so they can choose when to go live on the show. The calendar (loosely) follows my availability to go live, so the day chosen is really up to the guest, as well as the topic to talk about. We’ve talked about reading systems, heist movies, layoffs, and other things of interest. The guests really make the show what it is today, which I now call a variety talk show with some relation to tech either by topic or by guest.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/wireframe-set.jpeg&quot; alt=&quot;Behind the scenes on set&quot;/&gt;&lt;/p&gt;
&lt;h2 id=&quot;going-live&quot;&gt;Going live&lt;/h2&gt;
&lt;p&gt;When I first started Wireframe, it worked like a normal podcast. It was audio-only even though I was recording to YouTube just for free media storage. If I remember correctly, Descript might have been able to auto-detect the videos I uploaded then, I don’t remember exactly. But what I did know was that I’d have to replay the episode I had just gone through again, which I didn’t enjoy at all. Even with text-based editing cutting the traditional editing time down significantly, just going through the episode again after living it was a time suck. I needed to drop the editing altogether. I needed to do it live.&lt;/p&gt;
&lt;p&gt;At first, I avoided YouTube for this because the engagement there is questionable at best. People can hide behind fake names and bot accounts. I really wanted to have a professional-feeling show with professionals tuning in. That’s why I originally went live on LinkedIn. For the first few episodes, this worked OK but not great. Until one day…&lt;/p&gt;
&lt;p&gt;I went to go live and LinkedIn just wouldn’t. I thought it might have been some setting on my equipment, but it wasn’t. LinkedIn just isn’t a first-class platform to go live. I knew that if I was going to have scheduled guests in the future, I couldn’t use a platform that was just going to fail for no reason. I needed something that was made for video, so I went back to YouTube.&lt;/p&gt;
&lt;p&gt;I will say I’ve considered dual-streaming to LinkedIn since then, but since it’s clunky to set up, I’ve been dragging my feet on it. The one-hundredth episode of Wireframe Live is coming soon, so maybe I’ll make something special out of it. 👀&lt;/p&gt;
&lt;h2 id=&quot;post-production&quot;&gt;Post-production&lt;/h2&gt;
&lt;p&gt;Just like the original show, I didn’t want to go through a lot of post-production. After editing the show, I’d manually upload the audio to &lt;a href=&quot;https://www.acast.com&quot;&gt;Acast&lt;/a&gt; and then it would eventually be available through the RSS feed. Once it was available, I had an admin page on the original website that would help me create the related webpage for the episode. I needed to provide the &lt;code&gt;.srt&lt;/code&gt; file, and it would render the &lt;code&gt;.md&lt;/code&gt; from that for Astro to present.&lt;/p&gt;
&lt;p&gt;Since then, my post-production process has changed significantly. Since YouTube is the main host of the content, I don’t really need to have individual pages on my website for these things. YouTube does a better job with this ecosystem, including searchable transcripts and comments.&lt;/p&gt;
&lt;p&gt;I still liked having an audio version of the show, but I didn’t want to manually upload each episode. I wanted some service that would find the latest video and make a podcast episode out of it. The service I found to do this is &lt;a href=&quot;https://www.podbean.com/&quot;&gt;PodBean&lt;/a&gt;. It also makes syndication to everyone’s favorite listening apps very easy, mostly one-click connections. This service also produces the audio RSS feed for any other services that a person might connect. At the time of this writing, &lt;a href=&quot;https://designsyshouse.podbean.com/&quot;&gt;the podcast version&lt;/a&gt; has 2.5K downloads, which I think is pretty good since I don’t promote the audio version of it at all.&lt;/p&gt;
&lt;p&gt;Early on, I noticed it was really hard to get people to watch the show live. One of the pieces of feedback I got about it was “you’re trying to feed them an entire cake when you haven’t given them a taste yet.” That was a note about how it’s common for longer videos to have shorter clips that highlight certain moments. I thought this was a good idea to grow interest in the larger videos, but traditionally, this would also go back to the editing issue. Luckily, I found &lt;a href=&quot;https://www.opus.pro/&quot;&gt;OpusClip&lt;/a&gt;, which is not only able to find good clips from the video provided but can also automatically find the video uploaded to a particular channel. That means all I need to do is review the clips that are found and schedule them. It takes roughly 20 minutes the day after the show to allow people to get the gist of the show in a few seconds.&lt;/p&gt;
&lt;p&gt;The last thing that happens after the show is the YouTube description update. For a long time, I just ignored this, but eventually felt that the one function I worked on for a while in the original site, jumping to a section in the show, would be helpful for people looking for a certain topic in the show quickly. Traditionally, this would require at least reviewing the transcript to determine the topics that were spoken about. However, this is a place where AI has really helped. I have a small GitHub workflow that checks the YouTube API for the latest live show, grabs the transcript and sends it to Gemini for the chapters and summary, and then goes back to YouTube to update the description.&lt;/p&gt;
&lt;h2 id=&quot;reflection&quot;&gt;Reflection&lt;/h2&gt;
&lt;p&gt;I’ll say the &lt;a href=&quot;https://www.youtube.com/watch?v=XeHgLqshH2A&quot;&gt;conversation I had with Dan Mall yesterday&lt;/a&gt;, who recently started &lt;a href=&quot;https://www.youtube.com/@DanMallTeaches&quot;&gt;his own YouTube journey&lt;/a&gt;, gave me some inspiration for next steps. At the end of the show, we talked about thumbnails, and while you might think this would have just been a few nerds talking about how to optimize this, the real advice was that if you want great results, you need to put in great effort. All the effort I put in to go live could be wasted on poor packaging (i.e., the thumbnail). All of the time-saving I do could be revisited to improve the quality in areas where it’s lacking. The one thing that is missing is feedback.&lt;/p&gt;
&lt;p&gt;So if you have feedback on the show, I’d love to hear it. You can leave it as a &lt;a href=&quot;https://www.youtube.com/@designsyshouse/streams&quot;&gt;comment on a video&lt;/a&gt; or find me on the socials. If you’re daring, maybe you can give me the feedback live on the show and I’ll improve it right there. Could be fun?&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>community</category><category>events</category></item><item><title>Not tokens</title><link>https://blog.damato.design/posts/not-tokens</link><guid isPermaLink="true">https://blog.damato.design/posts/not-tokens</guid><description>A few values that should be captured in another way</description><pubDate>Tue, 21 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the years, I’ve seen some folks choose to create tokens such as &lt;code&gt;--color-black&lt;/code&gt;. And those tokens get completely inverted in dark mode, which confuses teams, who then reach for something more semantic. The funny thing is, designers say black but &lt;a href=&quot;https://ianstormtaylor.com/design-tip-never-use-black/&quot;&gt;don’t often mean black&lt;/a&gt;. More often, they expect a color like &lt;code&gt;#111&lt;/code&gt; or &lt;a href=&quot;/posts/eigengrau/&quot;&gt;eigengrau&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One reason people reach for primitive tokens like this is to &lt;a href=&quot;/posts/peoples-primitives/&quot;&gt;communicate complex values&lt;/a&gt;. Trying to say something like hash-one-one-one is cumbersome so we give it a name. The token bridges the gap between a raw hex code and what it represents. That’s useful but not every value needs that bridge. Some values already communicate exactly what they are. And when you tokenize something that’s already clear, you’re not adding meaning. You’re organizing for the sake of organizing.&lt;/p&gt;
&lt;p&gt;The other reason is flexibility. A token represents a decision that &lt;em&gt;could&lt;/em&gt; change. When you tokenize a value that has no reason to change, you’re not creating flexibility. You’re creating a false promise. You’re telling your team “this might be different someday” when it won’t be. And that false promise leads to one of two outcomes: either the team treats the token as sacred and never questions it, or they start questioning every token and lose trust in the system entirely.&lt;/p&gt;
&lt;h2 id=&quot;zero&quot;&gt;Zero&lt;/h2&gt;
&lt;p&gt;If you have no border or no spacing, creating a token called &lt;code&gt;0&lt;/code&gt; is not helpful because it’s not going to change. At what point is &lt;code&gt;--width-0&lt;/code&gt; going to be anything other than &lt;code&gt;0&lt;/code&gt;? It won’t and it subverts expectations.&lt;/p&gt;
&lt;p&gt;Unlike a hex code or a pixel value from a scale, &lt;code&gt;0&lt;/code&gt; doesn’t need a token to explain what it means. It already communicates perfectly. When a developer writes &lt;code&gt;margin: 0&lt;/code&gt;, they’re making a clear declaration: “I want no margin here.” Wrapping that in &lt;code&gt;var(--spacing-0)&lt;/code&gt; doesn’t add clarity. It adds indirection to a value that was already self-evident. And it bloats your token set with entries that serve no design decision.&lt;/p&gt;
&lt;p&gt;If you’re using zero tokens to maintain consistency in a scale (like &lt;code&gt;--spacing-0&lt;/code&gt; through &lt;code&gt;--spacing-8&lt;/code&gt;), consider whether that consistency is actually helping anyone. A scale exists to give people meaningful options to choose from. The choice between &lt;code&gt;4px&lt;/code&gt; or &lt;code&gt;8px&lt;/code&gt; is trying to feel out the relationship between objects. Zero is more specific, saying the relationship couldn’t be closer. That choice is more deliberate than trying find a value from a scale.&lt;/p&gt;
&lt;h2 id=&quot;transparent&quot;&gt;Transparent&lt;/h2&gt;
&lt;p&gt;Choosing to make something transparent is similar to choosing zero. Transparency is the absence of color, the same way zero is the absence of dimension. And like &lt;code&gt;0&lt;/code&gt;, the keyword &lt;code&gt;transparent&lt;/code&gt; already says exactly what it means. There’s no complex value to decode. A token like &lt;code&gt;--color-transparent&lt;/code&gt; will never resolve to anything other than &lt;code&gt;transparent&lt;/code&gt;, and if it did, it would break every context where it’s used.&lt;/p&gt;
&lt;p&gt;Think about where transparent typically shows up. You might use it on a ghost button to let the background behind it show through, or on an overlay’s initial state before it fades in. In both cases, the transparency &lt;em&gt;is&lt;/em&gt; the design expectation. It’s not a value that varies by theme or context. It’s a deliberate choice to show nothing.&lt;/p&gt;
&lt;p&gt;There’s also a practical concern. When teams see &lt;code&gt;--color-transparent&lt;/code&gt; in a token set, they sometimes assume it’s there to be overridden per theme, similar to other color tokens. That assumption leads to bugs. Someone decides that transparent should become &lt;code&gt;rgba(0, 0, 0, 0.05)&lt;/code&gt; in a specific context, and suddenly every ghost button has a faint background nobody asked for.&lt;/p&gt;
&lt;h2 id=&quot;full-rounding&quot;&gt;Full rounding&lt;/h2&gt;
&lt;p&gt;This is one of the most common tokens I see. It’s usually meant to be applied as &lt;code&gt;border-radius: var(--rounding-full)&lt;/code&gt; where &lt;code&gt;--rounding-full&lt;/code&gt; is set as some large value like &lt;code&gt;9999px&lt;/code&gt;. I ask the same question as the earlier examples above: do you ever expect this value to change?&lt;/p&gt;
&lt;p&gt;The value &lt;code&gt;9999px&lt;/code&gt; has always smelled like a magic number. It works because browsers clamp &lt;code&gt;border-radius&lt;/code&gt; to half the element’s shortest side, so any sufficiently large number produces a pill shape. But “sufficiently large” and “correct” are different things. There’s no specification behind &lt;code&gt;9999&lt;/code&gt;. It’s an arbitrary value that happens to be big enough. Now that we have the CSS keyword &lt;code&gt;infinity&lt;/code&gt;, we can replace it with something that actually means what we intended.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.rounding-full&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    border-radius&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(infinity &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But even with a better value, full rounding still doesn’t belong in a token. The decision to fully round something is made very close to the component. A pill-shaped tag, an avatar, a toggle thumb. These components know they need full rounding because of what they are, not because of what theme they’re in. A theme token implies that full rounding is a decision that lives far away from the component and might change across contexts. It won’t. An avatar is round in light mode and dark mode. A toggle thumb doesn’t become a squircle when the density changes.&lt;/p&gt;
&lt;p&gt;When full rounding lives in a theme-level token, it also gives teams the impression that it’s part of a scale alongside &lt;code&gt;--rounding-sm&lt;/code&gt;, &lt;code&gt;--rounding-md&lt;/code&gt;, and &lt;code&gt;--rounding-lg&lt;/code&gt;. But it’s not on the same spectrum. Those values represent varying degrees of a design decision. Full rounding represents a specific component-level choice. Treating them as peers in the same scale conflates two different kinds of decisions.&lt;/p&gt;
&lt;p&gt;I’m also afraid that making concentric rounding semantic is &lt;a href=&quot;/posts/meaningless-curves/&quot;&gt;practically impossible&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-underlying-principle&quot;&gt;The underlying principle&lt;/h2&gt;
&lt;p&gt;The question to ask before creating any token is simple: &lt;em&gt;will this value ever need to change independently of the property it’s applied to?&lt;/em&gt; If the answer is no, you don’t have a design decision. You have a constant. And well-named constants don’t need the overhead of a token system.&lt;/p&gt;
&lt;p&gt;Tokens should capture intent that varies. Color tokens vary by expression. Spacing tokens might vary by density. Typography tokens might vary by theme. That variability is what gives tokens their power. When you dilute your token set with values that can never vary, you make it harder for you to maintain your token system. Not to mention, a value like &lt;code&gt;0&lt;/code&gt; will be less bytes than any token you might want to introduce. The bottom line is you should weigh out how you expect the token to be used against the expected maintenance of that token over time. Is this an area where we can hard code the value like &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;transparent&lt;/code&gt;? Does the ability for this to be a circle hinge on the theme or the person choosing to make it circular?&lt;/p&gt;
&lt;p&gt;Do not reach to make another token first. Consider the scientific method; set the value and find out.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>tokens</category><category>css</category><category>naming</category></item><item><title>Theming atproto</title><link>https://blog.damato.design/posts/theming-atproto</link><guid isPermaLink="true">https://blog.damato.design/posts/theming-atproto</guid><description>Taking Mise en Mode into the new web standard</description><pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the &lt;a href=&quot;https://www.youtube.com/watch?v=5YCBWuMoti0&quot;&gt;most recent Wireframe live show&lt;/a&gt;, &lt;a href=&quot;https://overreacted.io/&quot;&gt;Dan Abramov&lt;/a&gt; came on to talk about his excitement over the new &lt;a href=&quot;https://atproto.com/&quot;&gt;AT Protocol&lt;/a&gt;. The AT Protocol is a new web standard that allows for decentralized social networking. It is designed to be more inclusive and accessible than existing social media platforms.&lt;/p&gt;
&lt;p&gt;Before the chat, I only really knew the protocol as the layer informing Bluesky. I tried learning more about it when trying to integrate the new &lt;a href=&quot;https://standard.site/&quot;&gt;standard.site&lt;/a&gt; features for publications like this one. The standard.site integration results in an enhanced post on Bluesky when done properly. Except when I was looking at the docs, I was immediately overwhelmed by the perceived complexity of the integration. I was hoping it would just be a metatag to add to my site. There’s more involved, and admittedly I won’t know if it worked until I share this very post. Do it live!&lt;/p&gt;
&lt;p&gt;However, after that chat with Dan, I was inspired to go deeper. The way he puts it, this is trying to take back the web to what it used to be, people just sharing stuff they like. The one thing that stuck with me was that he said it’s like RSS. I &lt;em&gt;love&lt;/em&gt; RSS. The first full-stack application I ever built, &lt;a href=&quot;https://deltazeus.com&quot;&gt;deltazeus&lt;/a&gt;, was centered around RSS. I love being able to subscribe to content, have it aggregated, and the best part have it presented in the way I want to see it.&lt;/p&gt;
&lt;p&gt;Toward the end of the show, I mentioned that I could see something in this new world that helps standardize the way we theme our applications. As many folks know, I’ve spent a lot of time thinking about how theming could be solved once and for all so anyone could express themselves. Understanding the essence of the new protocol as a way to share data helped me make the connection that a theme &lt;em&gt;is&lt;/em&gt; data. So, why not share this too?&lt;/p&gt;
&lt;h2 id=&quot;a-new-standard&quot;&gt;A new standard&lt;/h2&gt;
&lt;p&gt;Last week, I started working on a new standard for theming based on my Mise en Mode approach. If you want to just dive right in, there’s a small documentation site at &lt;a href=&quot;https://standard.mode.place&quot;&gt;standard.mode.place&lt;/a&gt; and a repository for the lexicon at &lt;a href=&quot;https://tangled.org/mode.place&quot;&gt;tangled&lt;/a&gt; (the atproto alternative to GitHub). In this post, I’m going to explain some of the details of what I’ve put together and why I think this is a good approach to theming.&lt;/p&gt;
&lt;h3 id=&quot;app-author-viewer&quot;&gt;App, author, viewer&lt;/h3&gt;
&lt;p&gt;In the standard I declare three different roles: the app, authors, and the viewer. The app is the owner of a default look of the application. For example, if this was Bluesky, when a person visited the site, they’d see the Bluesky branding decisions on the site. The colors, typography, and other design decisions made by the Bluesky team. Nothing too groundbreaking yet. The small difference here is that the variables used to define the look of the app are standardized under this approach.&lt;/p&gt;
&lt;p&gt;If you’re unfamiliar with &lt;a href=&quot;https://mode.place&quot;&gt;Mise en Mode&lt;/a&gt;, you can first think of having variables that represent the purpose of something in the UI. It’s closest to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/system-color&quot;&gt;system colors&lt;/a&gt; such as &lt;code&gt;CanvasText&lt;/code&gt; or &lt;code&gt;ButtonFace&lt;/code&gt;. There’s a whole construction behind the variable names chosen here, and it’s taken several years to develop. If you’re familiar with design tokens, this’ll be very familiar. Mise en Mode uses design tokens but it is not just the tokens, it is the way we use those tokens to express a look in regions within an experience.&lt;/p&gt;
&lt;p&gt;That’s the next part, just like how &lt;a href=&quot;https://standard.site/docs/lexicons/theme/&quot;&gt;standard.site allows a small &lt;code&gt;theme&lt;/code&gt; object&lt;/a&gt; to define how their publication might look elsewhere, this new standard gives the same functionality with a few more knobs to control other elements that might appear over time. A publication author could create their own theme, and then the site adopting this new standard could choose to display the author’s theme in scoped areas of the experience where it is appropriate. In this way, there’s not much difference from what you might be seeing on Bluesky today. You’d see publications that adopt this with their own colors in the “view publication” button.&lt;/p&gt;
&lt;p&gt;The last part is where this really gets interesting. The viewer is an authenticated user with their own defined theme. When an application that adopts the standard is visited, the viewer’s theme is applied to the experience. This means that if a viewer has a preference for a dark mode, or a high contrast mode, or any other theme they might have defined, it will be applied to the experience. This is where the power of this standard really shines. &lt;strong&gt;It allows for a more inclusive and accessible web experience&lt;/strong&gt;, where users can define their own preferences and have them respected across different applications.&lt;/p&gt;
&lt;h3 id=&quot;scoped-themes&quot;&gt;Scoped themes&lt;/h3&gt;
&lt;p&gt;Just like how standard.site scopes the look of the publication to a part of the Bluesky feed, think about how you might want to do that &lt;em&gt;for any region&lt;/em&gt;. Maybe you have a music player and want the artist’s theme to be shown when the music is playing without affecting the rest of the website. For e-commerce, perhaps the brands’ products have their theme shown to support recognition. These are all decisions that the app can make about how it wants to present itself and the content. It could just as well choose &lt;em&gt;not&lt;/em&gt; to have these things change while still adopting the standard to allow theming.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/standard-authed.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;If you want to see how this might work in a webpage, &lt;a href=&quot;https://standard.mode.place/demo/&quot;&gt;I have a demo you can try out&lt;/a&gt;. It shows how different regions might be affected depending on how the app is configured and what is applied to those regions. In some cases, the app decides only &lt;em&gt;color&lt;/em&gt; should be updated, while the typography falls through from the higher scopes. This could allow for some combinations where you still want the site to have consistency but also allow visitors to have their own accessible (&lt;a href=&quot;https://blog.codinghorror.com/a-tribute-to-the-windows-31-hot-dog-stand-color-scheme/&quot;&gt;or just fun&lt;/a&gt;) color combinations.&lt;/p&gt;
&lt;h2 id=&quot;small-footprint-big-impact&quot;&gt;Small footprint, big impact&lt;/h2&gt;
&lt;p&gt;Mise en Mode suggests that you only need 33 variables to express nearly any look. This means that the amount of curation that a person needs to create their own look is small. While I do know this doesn’t cover &lt;em&gt;all&lt;/em&gt; areas of applications (for example, it does not cover data visualizations), it does cover the majority of the experience. I have plans to extend the standard as needed, but I think this is a good starting point. It’ll cover what standard.site is doing right now and leave plenty of room to play with other looks.&lt;/p&gt;
&lt;p&gt;Right now, I’m looking for feedback on the standard from the community. &lt;a href=&quot;https://discourse.atprotocol.community/t/theming-atproto/962&quot;&gt;I’ve made a post&lt;/a&gt; on the &lt;a href=&quot;https://discourse.atprotocol.community/&quot;&gt;ATProtocol Community forums&lt;/a&gt; about this work. Importantly, &lt;strong&gt;I haven’t published v1 of the protocol yet&lt;/strong&gt;, so this is all theoretical at the moment. &lt;a href=&quot;https://tangled.org/mode.place/lexicons/pulls/1&quot;&gt;The PR with the lexicon is here&lt;/a&gt;. If you’re in the thick of atproto right now, I’d love to hear your thoughts. Even if you aren’t but want to know how theming might support your experience, you’re welcome to join in the discussions.&lt;/p&gt;
&lt;p&gt;I’d also like to build out the docs site to be a UI that helps people create their own themes easily. The goal is to make it as easy as possible for anyone to express themselves and have their preferences respected across different applications. This is a big step towards a more inclusive and accessible web, and I’m excited to see how it evolves over time.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>design</category><category>css</category><category>tokens</category><category>community</category><category>inclusive design</category></item><item><title>Lawful Design</title><link>https://blog.damato.design/posts/lawful-design</link><guid isPermaLink="true">https://blog.damato.design/posts/lawful-design</guid><description>The least fun but most required part of our work.</description><pubDate>Tue, 09 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;I am not a lawyer and this is not legal advice. I am an experience architect and this is my perspective on how law influences how we design.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;There’s a moment in nearly every project where the work stops being about the user. Someone from legal joins the call, and suddenly the clean flow we spent weeks refining has to grow a checkbox, a banner, a paragraph of fine print, or an entire screen that exists only to protect the company. It’s easy to treat this as the enemy of good design. I’d argue the opposite. Legal requirements are constraints, and &lt;a href=&quot;https://webinars.zeroheight.com/videos/2e30a1a5-7a29-4c2a-8c0b-7a14736874b9&quot;&gt;constraints are where design actually happens&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The trap most teams fall into is treating law and experience as adversaries fighting over the same pixels. The lawyer wants to be defensible. The designer wants freedom. Both of those goals can be true at the same time, but only if the two disciplines are in the room together early instead of handing artifacts back and forth at the end. When legal shows up after the design is “done,” you get the worst of both. A bolted-on disclosure that satisfies no one and a flow the user doesn’t want to use ever again.&lt;/p&gt;
&lt;p&gt;The goal of this article is to walk through the places where law touches our work and show that legal expectations and low friction aren’t opposites. The best solution is almost always the one that achieves both.&lt;/p&gt;
&lt;h2 id=&quot;consent--agreement&quot;&gt;Consent &amp;amp; Agreement&lt;/h2&gt;
&lt;p&gt;Consent is the clearest example of how a design decision becomes a legal one. As an example, the difference between a &lt;a href=&quot;https://ironcladapp.com/journal/contracts/clickwrap-vs-browsewrap&quot;&gt;clickwrap and a browsewrap agreement&lt;/a&gt; is purely a matter of interaction design: clickwrap requires the user to take an affirmative action (“I agree”), while browsewrap assumes consent simply because the user kept using the site. Courts have repeatedly cared about exactly this distinction. In &lt;a href=&quot;https://en.wikipedia.org/wiki/Specht_v._Netscape_Communications_Corp.&quot;&gt;&lt;em&gt;Specht v. Netscape&lt;/em&gt;&lt;/a&gt;, the court refused to enforce terms that lived below the download button, finding that a reference to license terms on a “submerged screen” wasn’t enough to bind the user. The legal protection failed because the design failed. A checkbox the user actually had to check would have been the more appropriate pattern and held up in court.&lt;/p&gt;
&lt;p&gt;This is the part that should excite us rather than annoy us. The lawyer’s requirement (“we need provable, affirmative consent”) and the designer’s requirement (“don’t make the user feel cornered”) point to the same solution. Cookie banners are where this collaboration most visibly breaks down. When the &lt;a href=&quot;https://www.cnil.fr/en&quot;&gt;CNIL&lt;/a&gt; &lt;a href=&quot;https://www.dataprotectionreport.com/2022/02/rejecting-cookies-should-be-as-easy-as-accepting-cookies-new-sanctions-by-the-french-authority-cnil/&quot;&gt;fined Google €150 million and Facebook €60 million&lt;/a&gt; over their cookie consent, the violation wasn’t the absence of a “reject” option. It was that accepting took one click while rejecting took several. &lt;a href=&quot;https://www.deceptive.design/types/obstruction&quot;&gt;The asymmetry was the deceptive pattern.&lt;/a&gt; The lesson for us is precise: making the compliant choice harder than the convenient one is both bad design &lt;em&gt;and&lt;/em&gt; illegal. A symmetric banner with “Accept all” and “Reject all” side by side is the rare case where the most ethical layout, the lowest-friction layout, and the most defensible layout are the same layout.&lt;/p&gt;
&lt;h2 id=&quot;disclosures--disclaimers&quot;&gt;Disclosures &amp;amp; Disclaimers&lt;/h2&gt;
&lt;p&gt;Disclosures are where the phrase &lt;a href=&quot;https://www.ftc.gov/business-guidance/blog/2014/09/full-disclosure&quot;&gt;“clear and conspicuous”&lt;/a&gt; earns its keep, and that phrase is a design specification hiding inside a legal one. The &lt;a href=&quot;https://www.ftc.gov/business-guidance/resources/disclosures-101-social-media-influencers&quot;&gt;FTC’s guidance for influencers&lt;/a&gt; is remarkably blunt about it. A disclosure buried in a wall of hashtags, hidden behind a “more” link, or shown only briefly in a video doesn’t count. They specifically warn against placing an &lt;code&gt;#ad&lt;/code&gt; where people have to stop and click to see it. This is not a suggestion. This is a rule for information hierarchy. We are being told where to put the content on the screen.&lt;/p&gt;
&lt;p&gt;This is precisely the territory where a designer and a lawyer should be sketching together. Left alone, a lawyer will often default to maximum coverage: more words, more caps lock, more places where the disclaimer appears. But a disclaimer nobody reads is a liability dressed up as protection.&lt;/p&gt;
&lt;p&gt;The more thoughtful solution is to make the disclosure legible at the moment of decision. A “this is not investment advice” line is worthless at the bottom of a terms page and meaningful right next to the “Buy” button. The emerging wave of &lt;a href=&quot;https://artificialintelligenceact.eu/article/50/&quot;&gt;AI-generated content disclosure in the EU AI Act’s transparency rules&lt;/a&gt; will test this again. The teams that win won’t be the ones who slap a generic “made with AI” badge on everything; they’ll be the ones who design a disclosure that’s honest, present at the right moment, and doesn’t insult the user’s intelligence.&lt;/p&gt;
&lt;h2 id=&quot;notice--transparency&quot;&gt;Notice &amp;amp; Transparency&lt;/h2&gt;
&lt;p&gt;Notice is the discipline of telling the user what’s happening to their data, and it’s where “legally sufficient” and “actually understood” drift furthest apart. We’ve all clicked through a privacy policy nobody could finish reading. Technically compliant, practically meaningless. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Right_to_be_forgotten&quot;&gt;right to be forgotten (RTBF)&lt;/a&gt;, established in Europe by &lt;a href=&quot;https://en.wikipedia.org/wiki/Google_Spain_v_AEPD_and_Mario_Costeja_Gonz%C3%A1lez&quot;&gt;&lt;em&gt;Google Spain v. AEPD&lt;/em&gt;&lt;/a&gt;, gives a person the ability to demand erasure of their data, but a right the user can’t find is no right at all. The legal text creates the obligation; design decides whether it’s real.&lt;/p&gt;
&lt;p&gt;The best privacy work I’ve seen splits this into two separate pieces. One is the full policy, the long document the lawyers need. The other is the short note that appears right when something happens. Like the section that explains why an app wants your location before asking for it. Users need that second one and asking a single document to do both makes this perform badly.&lt;/p&gt;
&lt;p&gt;Push notification permission prompts are a common version of this. &lt;a href=&quot;https://developer.apple.com/documentation/apptrackingtransparency&quot;&gt;Apple’s App Tracking Transparency&lt;/a&gt; framework forced a generation of apps to actually ask before tracking, and the apps that survived the change were the ones that followed the prompt. Instead of firing the system permission dialog the instant the app opens (when the user has no context and immediately taps “Don’t Allow”), the thoughtful pattern is a “pre-permission” screen that explains the value first, then triggers the real prompt only when the user is likely to say yes. That’s a collaboration between the legal need for genuine, informed consent and the design need to not burn the user’s goodwill in the first three seconds. Notice done well is a feature, not a bug.&lt;/p&gt;
&lt;h2 id=&quot;accessibility--inclusion&quot;&gt;Accessibility &amp;amp; Inclusion&lt;/h2&gt;
&lt;p&gt;Accessibility is the one area where the legal requirement and the design ideal often point the same direction. The hard part has been getting organizations to act on it, and that took years of accessibility professionals fighting to be heard against teams who treated their work as optional. The law didn’t reveal some hidden truth; it gave that long-running advocacy a platform. In the United States, &lt;a href=&quot;https://en.wikipedia.org/wiki/Robles_v._Domino%27s_Pizza,_LLC&quot;&gt;&lt;em&gt;Robles v. Domino’s Pizza&lt;/em&gt;&lt;/a&gt; made it clear that the &lt;a href=&quot;https://www.ada.gov/&quot;&gt;Americans with Disabilities Act&lt;/a&gt; reaches the website and the app, not just the physical store. The Supreme Court declined to hear Domino’s appeal, leaving the court’s ruling in place. Years earlier, the &lt;a href=&quot;https://en.wikipedia.org/wiki/National_Federation_of_the_Blind_v._Target_Corp.&quot;&gt;National Federation of the Blind’s case against Target&lt;/a&gt; ended in a multimillion-dollar settlement and helped establish that an inaccessible storefront online carries real legal exposure.&lt;/p&gt;
&lt;p&gt;The message has often been “do accessibility or get sued.” The better reading is that the law finally caught up to what good designers have wanted to do. &lt;a href=&quot;https://www.w3.org/WAI/standards-guidelines/wcag/&quot;&gt;WCAG&lt;/a&gt; &lt;a href=&quot;https://www.boia.org/blog/is-wcag-required-by-the-ada&quot;&gt;isn’t a legal document&lt;/a&gt;, it’s a design and engineering standard that the law happens to point at. This is the rare section where the lawyer and the designer don’t need to negotiate a balance at all. The legally fortified version &lt;em&gt;is&lt;/em&gt; the more usable version. Captions help the deaf user and the person watching in a quiet office. Sufficient contrast helps the blind user and everyone outside in bright sun. &lt;a href=&quot;/posts/eigengrau/&quot;&gt;When I’ve argued that users should be able to choose colors that work for them&lt;/a&gt;, this wasn’t from a compliance angle, it was just a more considerate way of designing. Accessibility is the proof that constraints make the product better for people.&lt;/p&gt;
&lt;h2 id=&quot;identity--verification&quot;&gt;Identity &amp;amp; Verification&lt;/h2&gt;
&lt;p&gt;Verification requires the deliberate design of friction. The law sometimes &lt;em&gt;wants&lt;/em&gt; the user to slow down and prove who they are, and that’s a fundamentally different design problem than the most other areas of user experience. A &lt;a href=&quot;https://en.wikipedia.org/wiki/Know_your_customer&quot;&gt;Know Your Customer&lt;/a&gt; flow at a bank exists to stop fraud and money laundering, and accredited investor verification under &lt;a href=&quot;https://www.investor.gov/introduction-investing/investing-basics/glossary/accredited-investor&quot;&gt;Regulation D&lt;/a&gt; exists to keep certain risky offerings away from people the regulators have decided shouldn’t access them. You cannot design these to be frictionless, and you shouldn’t try.&lt;/p&gt;
&lt;p&gt;What you &lt;em&gt;can&lt;/em&gt; design is honest friction instead of dishonest friction. The age gate that asks “Are you 18?” with a single Yes button is bullshit. Everyone knows it won’t stop anyone. Either the law requires real verification, in which case build a real flow with real checks, or it requires a good-faith barrier, in which case at least make it a deliberate one. This is a place where designers should push lawyers as hard as lawyers push designers. The reflex in compliance is to add more steps “to be safe,” but every unnecessary verification step is a place where a legitimate user abandons.&lt;/p&gt;
&lt;p&gt;The collaborative question is: what is the &lt;em&gt;minimum&lt;/em&gt; proof the law actually requires, and how do we collect exactly that and nothing more? Over-collecting identity data isn’t just bad for conversion, it’s a liability the moment you’re breached. Restraint is both the better experience and the safer legal position.&lt;/p&gt;
&lt;h2 id=&quot;regulated-industries&quot;&gt;Regulated Industries&lt;/h2&gt;
&lt;p&gt;Some industries come pre-loaded with rules so specific that the script is essentially written for you. &lt;a href=&quot;https://www.hhs.gov/hipaa/for-professionals/privacy/index.html&quot;&gt;HIPAA&lt;/a&gt; dictates how a patient authorizes the release of their health information. The &lt;a href=&quot;https://www.consumerfinance.gov/rules-policy/regulations/1006/&quot;&gt;Fair Debt Collection Practices Act&lt;/a&gt; dictates what a debt collector must disclose and even supplies a model validation notice. In these spaces, the lawyer isn’t offering an opinion, they’re delivering the law, and the designer’s job shifts from “what should this say” to “how do we deliver legally mandated language without making the experience feel like the inside of a courtroom.”&lt;/p&gt;
&lt;p&gt;This is a unique design responsibility. Mandatory language tends to be dense, defensive, and written by and for lawyers. The craft is in the framing around it. Progressive disclosure so the user isn’t hit with everything at once, plain-language summaries sitting beside the legally required text rather than replacing it, and pacing that respects why the friction exists. A HIPAA authorization should feel like the patient is making an informed choice about their own records, not signing away rights they don’t understand. The collaboration here is translation. The lawyer guarantees the words that must appear and the designer guarantees the human on the other end actually comprehends what they’re agreeing to. Both of those have to be true, and only working together gets you there.&lt;/p&gt;
&lt;h2 id=&quot;intellectual-property&quot;&gt;Intellectual Property&lt;/h2&gt;
&lt;p&gt;Intellectual property is interesting because the design challenge isn’t usually a single user’s flow, it’s a system that has to serve two opposed parties fairly. A &lt;a href=&quot;https://en.wikipedia.org/wiki/Online_Copyright_Infringement_Liability_Limitation_Act&quot;&gt;DMCA takedown process&lt;/a&gt; has to let a rights holder report infringement &lt;em&gt;and&lt;/em&gt; let the accused file a &lt;a href=&quot;https://www.dmca.com/FAQ/What-is-a-DMCA-Counter-Notice&quot;&gt;counter-notice&lt;/a&gt;, while the platform sitting in the middle is trying not to lose its &lt;a href=&quot;https://en.wikipedia.org/wiki/Safe_harbor_(law)&quot;&gt;safe harbor&lt;/a&gt;. If either side’s path is too hard to find, the legal protection for both parties can fade along with user trust in the platform.&lt;/p&gt;
&lt;p&gt;This is also where over-enforcement becomes its own design failure. Automated systems built to satisfy copyright law, like content-matching at upload time, routinely sweep up fair use, parody, and outright false claims because the friction of fighting back is so high that most users just give up. A thoughtful counter-notice flow is a legal safeguard and a user-experience safeguard at once. It keeps the platform compliant while giving wrongly-flagged users a real, navigable path toward a resolution. The elegant version makes &lt;em&gt;both&lt;/em&gt; submitting a claim and contesting one feel legitimate and proportionate, rather than weaponizing friction against whichever party the platform would rather not hear from.&lt;/p&gt;
&lt;h2 id=&quot;commerce&quot;&gt;Commerce&lt;/h2&gt;
&lt;p&gt;If there’s one section that proves friction can be a legal liability rather than a shield, it’s commerce. For a long time the prevailing design pattern was to make signing up effortless and cancelling miserable, the so-called &lt;a href=&quot;https://www.deceptive.design/types/hard-to-cancel&quot;&gt;“roach motel.”&lt;/a&gt; Regulators have decisively turned on this. The FTC sued Amazon over what its own employees reportedly called the &lt;a href=&quot;https://www.ftc.gov/news-events/news/press-releases/2023/06/ftc-takes-action-against-amazon-enrolling-consumers-amazon-prime-without-consent-sabotaging-their&quot;&gt;“Iliad Flow”&lt;/a&gt;, a Prime cancellation process so deliberately convoluted it was named after a long, grueling epic. The whole premise of that enforcement is that asymmetric friction, easy in and hard out, is itself the harm.&lt;/p&gt;
&lt;p&gt;The regulatory direction has been less than desirable, which is exactly why design judgment matters more than following strict laws. The FTC finalized a &lt;a href=&quot;https://www.ftc.gov/news-events/news/press-releases/2024/10/federal-trade-commission-announces-final-click-cancel-rule-making-it-easier-consumers-end-recurring&quot;&gt;“click-to-cancel” rule&lt;/a&gt; in 2024 requiring that cancelling be as easy as signing up, only for the court to &lt;a href=&quot;https://www.lw.com/en/insights/eighth-circuit-vacates-ftc-click-to-cancel-rule-days-before-compliance-deadline&quot;&gt;void it&lt;/a&gt; in July 2025 days before it took effect. But the underlying principle didn’t disappear with the rule. State laws like &lt;a href=&quot;https://leginfo.legislature.ca.gov/faces/codes_displayText.xhtml?lawCode=BPC&amp;division=7.&amp;title=&amp;part=3.&amp;chapter=1.&amp;article=9.&quot;&gt;California’s Automatic Renewal Law&lt;/a&gt; and the federal &lt;a href=&quot;https://www.ftc.gov/legal-library/browse/statutes/restore-online-shoppers-confidence-act&quot;&gt;ROSCA&lt;/a&gt; still demand clear auto-renewal disclosure and a straightforward way out, and enforcement against deceptive subscription design continues regardless of any one rulemaking. The point for designers is to stop optimizing for the loophole. The symmetry of “as easy to leave as it was to join” is where both the law and the user are heading, and building toward it now is cheaper than retrofitting it after a lawsuit.&lt;/p&gt;
&lt;h2 id=&quot;stop-collaborate-and-listen&quot;&gt;Stop, collaborate, and listen&lt;/h2&gt;
&lt;p&gt;The thread running through all of this is that the designer and the lawyer are not negotiating over how much of the experience each one gets to ruin. They’re solving the same problem from two directions. The lawyer is trying to protect the company and the designer is trying to produce the best user experience. A clear path &lt;em&gt;is&lt;/em&gt; a defensible one. The cases where companies got sued, fined, or dragged through the FTC weren’t cases of too little legalese, they were cases of design that hid, tricked, or exhausted the user. &lt;strong&gt;The deceptive pattern and the legal violation keep turning out to be the same thing.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So, bring legal in at the wireframe (or I guess at the prompt), not right before the deploy. Ask the lawyer what the rule actually requires rather than what feels safe, and ask the designer how to deliver it so a real person understands. The least fun part of our work, the disclosures and the consent flows and the fine print, is also where the most underrated work lives. Legal requirements and a frictionless experience are not a tradeoff to be balanced. Done right, they are the same goal wearing two different job titles.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>design</category><category>organization</category></item><item><title>Conway&apos;s Law</title><link>https://blog.damato.design/posts/conways-law</link><guid isPermaLink="true">https://blog.damato.design/posts/conways-law</guid><description>Reflecting our culture through a transparent lens.</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://en.wikipedia.org/wiki/Conway%27s_law&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p data-astro-cid-arj5dyob&gt;[O]rganizations which design systems (in the broad sense used here) are constrained to produce designs which are copies of the communication structures of these organizations.&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://en.wikipedia.org/wiki/Conway%27s_law&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt; Melvin E. Conway&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;p&gt;I felt like I’ve heard of “Conway’s Law” before, but I think I was confused with &lt;a href=&quot;https://en.wikipedia.org/wiki/John_Horton_Conway&quot;&gt;John Conway&lt;/a&gt;. I thought the law had something to do with mathematics and his &lt;a href=&quot;https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life&quot;&gt;Game of Life&lt;/a&gt; and numerous videos on &lt;a href=&quot;https://www.numberphile.com/&quot;&gt;Numberphile&lt;/a&gt;. I was mistaken and didn’t realize how much I aligned to the idea first formally introduced in 1967, nearly 60 years ago.&lt;/p&gt;
&lt;p&gt;What &lt;a href=&quot;https://en.wikipedia.org/wiki/Melvin_Conway&quot;&gt;Melvin&lt;/a&gt; is saying above is similar to something that I’ve been saying for many years. Design systems should be a reflection of the people who are using them. However, in my experience this hasn’t been the case. There have been so many stories of the struggle of having the larger organization adopt a design system. Lots of different techniques and strategies developed and spoken about. However, I haven’t seen many truly succeed.&lt;/p&gt;
&lt;h2 id=&quot;what-is-success&quot;&gt;What is success?&lt;/h2&gt;
&lt;p&gt;We’d like to think that success is 100% adoption. Everyone in the organization is using design system resources, no deviations, no overrides, total happiness. We all know this isn’t realistic. But why isn’t it realistic?&lt;/p&gt;
&lt;p&gt;What would happen is some team would dream up some new pattern to try, and it wouldn’t be supported in the design system. There’s several directions this could take, including but not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Feature team builds it on their own from scratch and it lives with them.&lt;/li&gt;
&lt;li&gt;Design system team thoughtfully tries to include it in the design system.&lt;/li&gt;
&lt;li&gt;Feature team uses the pieces of the design system to create the new pattern.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first example seems undesirable from a design system maintenance standpoint. If every team has their own stuff, how could we possibly keep it consistent? How could we know they are aligned to best practices and standards?&lt;/p&gt;
&lt;p&gt;The second example seems undesirable from a feature team perspective. We don’t have time to wait for the design system team to prioritize, research, and ultimately deliver something we already know we need. Our team is fully capable of building this component. Why would we wait for another team who doesn’t feel the need like we do?&lt;/p&gt;
&lt;p&gt;And the third seems like a reasonable approach, until the feature team needs to dive into the design system’s inner workings, deep patterns and guidance, and strict rules. It’s taking longer to review this repository of information than initially thought. The feature would have been built faster if either the feature team did it themselves (without the guidance) or the design system team did it (with the guidance). It seems no matter how you slice it, friction is inevitable.&lt;/p&gt;
&lt;h2 id=&quot;stone-tablets&quot;&gt;Stone tablets&lt;/h2&gt;
&lt;p&gt;Unfortunately, there’s lots of design systems out there that we assume are successful because they are backed by large companies and even have their own branding. Material by Google, Polaris by Shopify, Spectrum by Adobe. However, many of these systems are &lt;a href=&quot;https://x.com/susie_dent/status/580652801912651776&quot;&gt;wonderclout&lt;/a&gt;. Brand and marketing tactics to suggest the company as a whole is onboard with these design systems and to get people excited about the organizational design strategy.&lt;/p&gt;
&lt;p&gt;As many of us in the profession know, &lt;a href=&quot;https://www.forbes.com/sites/jurgenappelo/2015/09/03/the-best-organizational-culture/&quot;&gt;culture eats strategy for breakfast&lt;/a&gt;. When our design system is delivered using stone tablets down from the hilltop, it is often rejected. Even if it has made its best attempt at accurately reflecting what the culture is doing. As design system maintainers, we can’t help ourselves to bias the deliverable with structure, and those small tweaks could be enough to turn your teams off to the design system. Even with the best intentions, and the largest carrot on a stick, the culture often will not truly accept this.&lt;/p&gt;
&lt;p&gt;In many cases, what we’ve done in practice is attempted to show and tell how things could be. We could have a consistent experience so users will trust the flow. We could all use the same components so feature teams don’t need to build anything new. We could make large branding changes in a few hours using design tokens. And the demos are often very enticing. However, once we try to make our work behave in the legacy systems and culture, breaking years of doing things because “that’s the way we’ve always done it” becomes a serious risk that teams aren’t often willing to take.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p data-astro-cid-duqfclob&gt;I find this especially clear in the world of &lt;a href=&quot;/posts/the-daily-vibe/&quot;&gt;AI generated “design systems”&lt;/a&gt;.  If you build your own, what culture are you fighting with other than your own? That’s why they feel more successful to the author. There’s no struggle and it’s precisely the way you know your team (ie., you) needs it.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;So what are we meant to do?&lt;/p&gt;
&lt;h2 id=&quot;chaos-reigns&quot;&gt;Chaos reigns&lt;/h2&gt;
&lt;p&gt;A few weeks ago, I had the opportunity to &lt;a href=&quot;https://uxdx.com/session/the-future-of-design-systems/&quot;&gt;debate the future of design systems with Alex Wilson&lt;/a&gt; at UXDX in New York. In the prep meeting for the session, Alex and I were trying to find areas where we disagree. The main area was specifically this: should teams be allowed to do whatever they want?&lt;/p&gt;
&lt;p&gt;Alex was on the side of the traditional design systems practitioner. This makes sense from what many of us have been doing in the field for years. Of course we can’t trust these teams with best practices, they are too deep in trying to make their feature successful. There is no way they can keep up with also trying to cross-reference what the other teams are doing. That’s the job of a design system maintainer, to help keep everyone stepping to the rhythm of the same drum.&lt;/p&gt;
&lt;p&gt;I am on the other side. I’ve already said that I’m &lt;a href=&quot;/posts/done-with-components/&quot;&gt;done with components&lt;/a&gt;. And it was then where I introduced the idea of the marketplace. This would be where everyone could simply add whatever components they wanted and it would immediately be available to everyone. If that produced 20 buttons, then so be it.&lt;/p&gt;
&lt;p&gt;I vividly remember Alex asking the audience if this seemed like chaos, and after the audience cheered, he said “that’s because it is.” And it’s true, it is chaos. But it’s only chaos because &lt;em&gt;your organization is chaotic&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What I’ve proposed is to show your dirty laundry to the whole organization. This is the direct opposite of peacocking a stone tablet design system. It’s showing all the cards. It shows where the cracks are and what we might want to address first. With a transparent marketplace, if leadership is comfortable with 20 different kinds of buttons, then it’s OK. If leadership sees value in reducing that to a more reasonable amount, then they will drive the initiative to reduce this set. The fact is that leadership must have the data to act on. And if they cannot see the live audit of what the design organization is doing, they cannot address it.&lt;/p&gt;
&lt;p&gt;This comes from years of seeing support for child component libraries meant to support feature teams. Where the feature team is now not only responsible for their feature, but also the infrastructure to support their own child component libraries and often fail at keeping them in sync with the parent design system resources. They’re often incompatible. The design system team often has the runway for upgrades and improvements while the feature team is struggling to be just in front of deprecated projects.&lt;/p&gt;
&lt;p&gt;If we had a mature and transparent marketplace, I believe that marketplace would truly reflect the culture of the organization. How it &lt;em&gt;wants&lt;/em&gt; to operate, not its ideal state but its accurate state. From here, we might even see &lt;a href=&quot;/posts/design-attractors/&quot;&gt;design attractors&lt;/a&gt;; a self-organized design system that comes from the reflection of the organization’s culture.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;
&lt;p&gt;I know this is a large pill to swallow. I’m suggesting that we pivot from imposing order to embracing chaos. However, I’d recommend thinking about it in a different way. I know that the most helpful channel of support is &lt;em&gt;always&lt;/em&gt; the design system team. So, let’s think about how we can help our peers with this new idea. Let’s create the infrastructure for them to make it as easy as possible to create that new timeline component. Let’s get it on the marketplace as soon as it’s available in production for everyone to see it. Let’s make it easy to discover what things other teams have already built. Let’s have metrics around usage, testing, accessibility, localization, etc. that are already showing the quality of our work. &lt;strong&gt;Stop trying to own the artfacts, own the infrastructure.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If the organization values quality pieces, efforts will be made to improve those pieces. If the organization doesn’t, the product may suffer because of it. The bottom line is, the organization can’t know how well it’s doing without this transparency. Otherwise, it’s just another pretty system for our teams to ignore.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>organization</category></item><item><title>Eigengrau</title><link>https://blog.damato.design/posts/eigengrau</link><guid isPermaLink="true">https://blog.damato.design/posts/eigengrau</guid><description>Close your eyes, what do you see?</description><pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Doomscrolling last week, I came across a post with the word “eigengrau”. The post said that it was “the color you see when you close your eyes”. It even included a hex color associated with it: &lt;code&gt;#16161D&lt;/code&gt;. That immediately made me think of all the posts I’ve seen on the Internet about how you shouldn’t use &lt;code&gt;#000&lt;/code&gt; as black and instead use something softer like &lt;code&gt;#111&lt;/code&gt;. I was connecting the dots: if this is the color we see when we close our eyes, rather than the true absence of color, then it makes sense that this kind of black feels more comfortable. It’s more natural to our eyes.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;In my research on this topic, it seems less about color and more about the noise that you see when you close your eyes. How the brain processes the lack of light rather than a specific color.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;Part of me wanted to start a movement for a new HTML color named &lt;code&gt;eigengrau&lt;/code&gt;, mapped to &lt;code&gt;#16161D&lt;/code&gt;. At the moment, the closest HTML color keyword is &lt;code&gt;black&lt;/code&gt;, which is less desirable than something like &lt;code&gt;#111&lt;/code&gt; from my experience. However, it seems that this hex color is not accurate to what this is meant to represent.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Eigengrau&quot;&gt;Wikipedia entry for the term&lt;/a&gt; shows eigengrau occurring at luminances below approximately 10⁻⁵ cd/m² — that’s 0.00001 &lt;a href=&quot;https://www.nist.gov/si-redefinition/candela&quot;&gt;candelas&lt;/a&gt; per square meter. That’s a &lt;em&gt;physical light intensity threshold&lt;/em&gt;, not a color value like hex. In other words, science measures eigengrau as &lt;em&gt;luminance&lt;/em&gt; not color.&lt;/p&gt;
&lt;p&gt;This doesn’t mean that we can’t get close using CSS. If we use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/color&quot;&gt;&lt;code&gt;color()&lt;/code&gt; function&lt;/a&gt; with the &lt;a href=&quot;https://en.wikipedia.org/wiki/CIE_1931_color_space&quot;&gt;&lt;code&gt;xyz&lt;/code&gt; color space&lt;/a&gt;, we can write it in this way:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;--eigengrau: color(xyz pow(10&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -5) pow(10&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -5) pow(10&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -5));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value of 10⁻⁵ (written above as &lt;code&gt;pow(10, -5)&lt;/code&gt;) comes directly from the findings on the research on eigengrau. It is very possible your monitor won’t display this, and might even turn off the pixels entirely, making &lt;code&gt;#000&lt;/code&gt; that we were expecting to avoid.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;One caveat: CSS XYZ values are normalized relative to display white rather than measuring absolute luminance in cd/m², so this is an approximation rather than a literal translation of the threshold. The result would still be very close to what we see when we close our eyes in a dark environment, depending on the display and the ambient lighting conditions. It would be a more accurate representation of eigengrau than the hex value that was initially associated with it.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;So where did this hex code come from? In the &lt;a href=&quot;https://en.wikipedia.org/wiki/Talk:Eigengrau&quot;&gt;discussion associated with the Wikipedia entry&lt;/a&gt;, there’s a lot of arguments about where the original hex code was introduced. The oldest they’ve discovered is from 2009. The earliest I can find is &lt;a href=&quot;https://www.meutedio.com/2005/12/tabela-de-cores.html&quot;&gt;this website with a timestamp of 2005&lt;/a&gt;. Scrolling through the list, you can find the hex &lt;code&gt;#16161D&lt;/code&gt; and hovering over the row will show the name “Eigengrau”. It is written in the row HTML as &lt;code&gt;title=&amp;quot;Eigengrau&amp;quot;&lt;/code&gt;. Interestingly, there are other names tucked into the HTML for other colors like “Aureolin” and “Feijoa” that I’ve never heard of but appear in other, more recent, color lists around the web.&lt;/p&gt;
&lt;p&gt;I have reached out to the author of the website for more information. It is currently unclear if this page has been updated since 2005. However, I’m not convinced that this is the source for all of these color names. I’ll update the post here if I hear back from the author.&lt;/p&gt;
&lt;p&gt;This whole exercise about debating whether &lt;code&gt;#000&lt;/code&gt; is too harsh, looking for a softer alternative touches on a larger question about contrast. A &lt;a href=&quot;https://www.nature.com/articles/s41598-018-28904-x&quot;&gt;research study&lt;/a&gt; found that “white text from a black screen could inhibit myopia, while black text on white background may stimulate myopia,” recommending against pure black on pure white due to the contrast. &lt;a href=&quot;https://skeptics.stackexchange.com/questions/41578/is-black-text-on-white-background-difficult-for-readers-with-dyslexia&quot;&gt;Skeptics push back&lt;/a&gt; on whether color was really the main factor, but that uncertainty cuts both ways.&lt;/p&gt;
&lt;p&gt;The better answer is to stop asking which softer black is universally correct. Now that personalization is a key feature of the web, the ability for users to choose the colors that work best for them matters more than whichever default a designer picks. A mechanism for users to choose their own colors and other presentational settings is key. This is especially true for folks with visual impairments or other conditions that make certain color combinations difficult to read.&lt;/p&gt;
&lt;p&gt;At best, the original interest in a name for something that I didn’t know had a name was fun. Even if it seems to be largely misunderstood. I’ll say it’s definitely a better name than “brain gray” 🧠&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category><category>color</category><category>accessibility</category><category>inclusive design</category><category>naming</category></item><item><title>Tiny toggle</title><link>https://blog.damato.design/posts/tiny-toggle</link><guid isPermaLink="true">https://blog.damato.design/posts/tiny-toggle</guid><description>Switching between light and dark as lazily as possible.</description><pubDate>Tue, 05 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over at the &lt;a href=&quot;https://ds.house&quot;&gt;Design Systems House website&lt;/a&gt;, I made a small toggle for light and dark mode. While there’s an abundance of posts describing this, I didn’t find any that made it into a compact script of a few lines. Here’s how I did it, and some additional considerations to keep in mind.&lt;/p&gt;
&lt;h2 id=&quot;the-script&quot;&gt;The script&lt;/h2&gt;
&lt;p&gt;This is all the JavaScript that makes the toggle work:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; LOCALSTORAGE_KEY&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;dark&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; $toggle&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; document&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.getElementById&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;toggle&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;matches&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.matchMedia&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; stored&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;localStorage&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.getItem&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;LOCALSTORAGE_KEY&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;$toggle&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.checked &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; stored &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;!==&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; stored &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;true&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; matches;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;$toggle&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;?.addEventListener&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;change&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (ev) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    window&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;localStorage&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.setItem&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;LOCALSTORAGE_KEY&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; String&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;ev&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;target&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.checked));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This assumes that there is a &lt;code&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot; id=&amp;quot;toggle&amp;quot;&amp;gt;&lt;/code&gt; element in the HTML.
Let’s go over the lines:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We define a constant for the &lt;code&gt;localStorage&lt;/code&gt; key.&lt;/li&gt;
&lt;li&gt;We get the toggle element from the DOM.&lt;/li&gt;
&lt;li&gt;We check if the user has a preference for dark mode using &lt;code&gt;matchMedia&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We check if there’s a stored value in &lt;code&gt;localStorage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We set the toggle’s checked state based on the stored value or the user’s preference.&lt;/li&gt;
&lt;li&gt;We add an event listener to the toggle.&lt;/li&gt;
&lt;li&gt;When the toggle changes, we store the boolean value as a string in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Importantly, we assume that the initial presentation is “light” and explicitly look for if there’s any setting for “dark”. The first thing we look at is if it was set as “dark” for this site using the toggle from a previous session. If not, we check what the OS preference is. This is setting the initial state of the toggle element, being checked if “dark” is picked up from either of those conditions. Then we listen for changes to the toggle and only update the &lt;code&gt;localStorage&lt;/code&gt; value when the toggle is changed. This overrides the OS user preference when set.&lt;/p&gt;
&lt;h2 id=&quot;the-styles&quot;&gt;The styles&lt;/h2&gt;
&lt;p&gt;Now with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:has&quot;&gt;baseline &lt;code&gt;:has()&lt;/code&gt; selector&lt;/a&gt;, getting this to behave is pretty easy with a single selector to introduce a new expression:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:has&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;#toggle:checked&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /* dark mode styles */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a very simple way to apply styles based on the state of the toggle. The &lt;code&gt;:has()&lt;/code&gt; selector allows us to check if the toggle is checked and apply styles accordingly. This way, we don’t need to add any additional classes or attributes to the body element to indicate the current mode. We can just rely on the state of the toggle itself.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;I’m deliberately avoiding the HTML of the toggle here. If you really want to know what the HTML is, you can grab it from &lt;a href=&quot;view-source:https://ds.house/&quot;&gt;the source over at ds.house&lt;/a&gt;. I think the JavaScript and CSS used are more important for this article.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;about-system-settings&quot;&gt;About system settings&lt;/h2&gt;
&lt;p&gt;When I initially made this kind of toggle for light/dark, I thought about it differently. Instead of light/dark, I wanted to toggle between “system” and “opposite of system”. In other words, if the OS was set as “dark”, the toggle would then either align to what was set in the system or be the opposite when toggled. Visually in terms of presentation, there would be no difference. If your system was set to “dark”, then by default the site would be dark. If your system was “dark” and you wanted this site to be “light”, you would switch to the “opposite of system” option.&lt;/p&gt;
&lt;p&gt;This helps avoid the problem of setting a specific flag on the site once set. In the implementation above and at ds.house, once we store the state expected by the user for the website, there’s no way of resetting it back to the state of not being set. It’s either “light” or “dark” (or &lt;code&gt;false&lt;/code&gt;/&lt;code&gt;true&lt;/code&gt; in the code). With the “system” and “opposite of system” approach, you can always reset to “system” to align with the OS preference. In this way, setting &lt;code&gt;false&lt;/code&gt; is the same as not setting anything. This is a nice way to allow users to easily reset their preference without needing to clear &lt;code&gt;localStorage&lt;/code&gt; or have a separate “reset” button.&lt;/p&gt;
&lt;p&gt;There’s a few problems with this, one of them being that I could never figure out a good way to explain this setting. “System” as the default is fairly straightforward, but “opposite of system” is a bit more difficult to understand. The other problem is that it doesn’t really align with the mental model of how users think about light/dark mode. They think in terms of light and dark, not “system” and “opposite of system”. So I went with the more straightforward approach of just toggling between light and dark.&lt;/p&gt;
&lt;p&gt;If you’d rather use a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; instead of a &lt;code&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot;&amp;gt;&lt;/code&gt;, you can modify the script to accommodate that by referencing &lt;code&gt;.selected&lt;/code&gt; instead of &lt;code&gt;.checked&lt;/code&gt; in the appropriate places. Just make sure your HTML has matching values for &lt;code&gt;localStorage&lt;/code&gt; and the options in your &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;. You might want this if you want to expose “system”, “light”, and “dark” as options.  &lt;a href=&quot;https://kilianvalkhof.com/2020/design/your-dark-mode-toggle-is-broken/&quot;&gt;Kilian Valkhof recommends&lt;/a&gt; having all three and &lt;a href=&quot;https://www.bram.us/2022/05/25/dark-mode-toggles-should-be-a-browser-feature/#the-problem-with-dark-mode-toggles&quot;&gt;Bramus Van Damme explains&lt;/a&gt; the different possible levels of setting this information.&lt;/p&gt;
&lt;h2 id=&quot;knowing-your-audience&quot;&gt;Knowing your audience&lt;/h2&gt;
&lt;p&gt;If you’re wondering why I opted to &lt;em&gt;not&lt;/em&gt; expose the “light”, “dark”, and “system” options at ds.house, it’s because no person is spending a signficant amount of time on that website. Enough that they’ll visit everyday to be productive. Setting whether or not “light” or “dark” is right for this person is most likely a one-time thing specifically for a single visit. It is unlikely that a user will want the presentation of this site to change over the course of a day, unlike productivity applications like email clients where you’ll visit multiple times a day. Those experiences will expect smarter customization options.&lt;/p&gt;
&lt;p&gt;For a small website that gets a visit every once in a while, keeping it simple might be the better approach. But it’s important to know the tradeoffs. And if you ever come up with a good word for “opposite of system”, do let me know.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category><category>color</category><category>tokens</category></item><item><title>The Daily Vibe</title><link>https://blog.damato.design/posts/the-daily-vibe</link><guid isPermaLink="true">https://blog.damato.design/posts/the-daily-vibe</guid><description>No, you didn&apos;t vibe design/code a system in a few hours</description><pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;I vibe design/coded a design system in a few hours!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I see a post like this everyday on LinkedIn. In a world where AI is taking the industry by storm, it’s meant to show how powerful these tools are. But it also shows how little the industry understands about the work that goes into design systems.&lt;/p&gt;
&lt;h2 id=&quot;design-systems-are-for-people&quot;&gt;Design systems are for people&lt;/h2&gt;
&lt;p&gt;This is a quote by &lt;a href=&quot;https://www.jina.me/&quot;&gt;Jina Anne&lt;/a&gt; and the word “people” is important. First, a design system is not for a single person. It’s the reason why none of the sites I create have a design system. They have reusable resources, but not a system. The reason why they don’t have a system is because many of my personal projects are built by a single person; me. I don’t need to communicate the do’s and don’ts with anyone else. Do I have pieces of design systems like variables and components? Yes, but these things do not immediately make a design system. What I’m missing is what I find to be the defining quality of a system: &lt;em&gt;agreement&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To get agreement, you need to have people. People who have different opinions on design, the way things should work, the way we represent ourselves. This is where the role of a design system maintainer comes in. Are we experts in tokens and components? Yes, but the hidden role that we discuss as a practice is that of a diplomat. We need to be able to communicate with different teams, different stakeholders, and different people. We need to be able to understand their needs and their perspectives, and synthesize that into a system that works for everyone. This is not something that can be done in a few hours, or even a few days. It takes time to build trust, to build relationships, and to build a system that people will actually use. I could have the perfect typography ramp in my eyes, but what good is it if it isn’t adopted? At best it’s a proof of concept or something for my personal work, but getting people to agree on it and use it, that’s the hard part. That’s the beginnings of a design system.&lt;/p&gt;
&lt;p&gt;So, if you made a styleguide for yourself in a few hours and that helps you visualize the plan for your project, that’s great! But I’d argue it isn’t a system unless you have stakeholders. People who can affect the decisions of the system, and people who can be affected by the decisions of the system. If you don’t have those people, then you don’t have a system. You have a styleguide, or a pattern library, or a component library, but you don’t have what I’d call a design system.&lt;/p&gt;
&lt;h2 id=&quot;how-to-vibe-a-design-system&quot;&gt;How to vibe a design system&lt;/h2&gt;
&lt;p&gt;Let’s say you want to really crack this nut. What does vibe creating a design system actually look like?&lt;/p&gt;
&lt;p&gt;From my perspective, the goal is to get the agreement of the stakeholders and make that available as a reusable resource. What this means is you need some way of collecting the decisions of the organization into a central location which is later used to inform design and development work over product lifecycles.&lt;/p&gt;
&lt;p&gt;One of the pieces to this puzzle is most likely some hosted &lt;a href=&quot;https://en.wikipedia.org/wiki/Model_Context_Protocol&quot;&gt;MCP&lt;/a&gt; server. This allows your AI tool to connect to a central location where it can look up resources of the design system in order to make informed decisions. This is the new “docs site” so to speak, except it’s not meant for humans to read, it’s meant for machines to read. This is where the agreement of the organization lives and how AI tools would reference what the agreements are for the organization. The challenge from here only becomes getting the organization to connect to the MCP using their tool of choice. This is no different from what we do today, promoting use of the system.&lt;/p&gt;
&lt;p&gt;The question is what does that agreement look like? Right now, there’s lots of talk about the &lt;code&gt;DESIGN.md&lt;/code&gt; file popularized by Google’s &lt;a href=&quot;https://stitch.withgoogle.com/&quot;&gt;Stitch&lt;/a&gt; project. This is a markdown file that is a hyped-up styleguide. It’s not a design system for the reasons we mentioned above. But it &lt;em&gt;could be&lt;/em&gt;. If that &lt;code&gt;DESIGN.md&lt;/code&gt; file was created by collecting the decisions of the organization, then that’s when it becomes a representation of a design system.&lt;/p&gt;
&lt;p&gt;The final piece to the puzzle is what getting agreement looks like. I’ve always said that a design system is a reflection of the people who use it. This means that if the organization wants purple gradient buttons, then it gets purple gradient buttons. However, other organizations have design systems teams that operate with stone tablets, carrying down the agreements from high atop a mountain of design wisdom. They might have gathered feedback from the organization, but the final decisions are curated by the design system team.&lt;/p&gt;
&lt;p&gt;In some of these cases, this is a good thing. This is the team responsible for making sure the resources provided by the system are of high quality and are consistent with the organization’s brand. In other cases, this can be a not-so-good thing. This can lead to a disconnect between the design system and the organization, where the design system is not actually serving the needs of the organization. This is where people outside of this team start going rogue, and where other decisions start making their way into the product without aligning to what was bestowed upon them. Stakeholders begin to take notice and might choose a side, defunding the design system because they are moving faster without it or because they vibed one up themselves. What’s so hard about making some color variables and a few components, especially now that generating them takes seconds? This isn’t agreement any longer, it’s just another vibe project gaining some temporary traction.&lt;/p&gt;
&lt;p&gt;The way to vibe a system is to capture what the organization is doing and making that available as a resource. This is the modern button audit. Traditionally, a design system blossoms from roaming around the product, gathering the decisions that have been made organically, and then making sense of those decisions as a collection of resources for the organization to use. This is that reflection idea from earlier. In areas where we find things in conflict, we get to the bottom of it. We ask stakeholders, complete research, and come to some conclusion on what the path forward is so that we’re all on the same page. Generally speaking, the design system team are moderators of that debate. We typically have very little stake in the outcome, as long as the final decision is beneficial to users. This is where we find out if the organization wants purple gradient buttons or not.&lt;/p&gt;
&lt;p&gt;So, what if the process of capturing the system was not a manual walk-around but a swarm of agents traversing the landscape of the product on a regular basis? As new decisions were made, they would be captured and considered a new design decision. This doesn’t necessarily mean that every small change is immediately a shared agreement, instead it provides a small amount of influence to how the design system evolves. As more people view other people’s decisions, they will themselves decide if that change they saw elsewhere is appropriate for them too. Now there’s &lt;em&gt;two&lt;/em&gt; places where that decision was made, gaining more influence on the design decisions. More people see this new direction and start adopting it, further influencing the decisions. Before long, the design system is truly reflecting what we’ve all individually decided to do. A &lt;strong&gt;self-fulfilling design system&lt;/strong&gt; that is truly a reflection of the organization, growing organically through the work that people are doing daily. This is the vibe design system, and it’s not something you can do in a few hours, but it is something that can be done with the right tools and the right approach.&lt;/p&gt;
&lt;p&gt;Admittedly, I don’t know what that looks like from a technical perspective. What does the infrastructure look at, how does it capture the decisions, and how do you fine tune the amount of influence each morsel of information has on the design system? What does the process of a rebrand look like? Do you turn the influence up to 11 for a specific file? I know some products are claiming that they take your existing system into account when vibing, but currently they are looking at existing resources. I don’t think any system is looking at the &lt;em&gt;product&lt;/em&gt; as a reflection of the system. But that’s &lt;em&gt;exactly&lt;/em&gt; what they should be doing. It’s what the design system team has been doing, or at least trying to do.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;I know what I’m proposing suggests that the design system team is no longer necessary. What I’d like to think is that it is no longer necessary &lt;em&gt;in the traditional role we’ve had&lt;/em&gt;. This is yet another example of how our industry is shifting in the world of AI. Design system practitioners need to re-adjust their positions. Change is uncomfortable, but it’s also an opportunity to grow and evolve. Our new role is not to be librarians but to be conductors. The music is going to play with or without us. We can help set the tempo.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;So, if you want to make a vibe design system, get it to look at the existing product and make the &lt;code&gt;DESIGN.md&lt;/code&gt; from that. That’s where the people are and their design decisions. Keep that up-to-date with how the people are working and the growth of the product. And the thing that you generated in a few minutes that you call a design system, it’s not. However, with a few people and some smarter infrastructure, it could become one.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>naming</category><category>community</category><category>organization</category></item><item><title>Pretext review</title><link>https://blog.damato.design/posts/pretext-review</link><guid isPermaLink="true">https://blog.damato.design/posts/pretext-review</guid><description>Another distraction from improving user experience</description><pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This weekend a project called &lt;a href=&quot;https://github.com/chenglou/pretext&quot;&gt;pretext&lt;/a&gt; started making waves by &lt;a href=&quot;https://github.com/chenglou&quot;&gt;Cheng Lou&lt;/a&gt;. Here’s a gif that has been blowing up social media:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/pretext-dragon.gif&quot; alt=&quot;Directing a dragon with your cursor through a body of text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;There’s no doubt about how impressive this demo is. The speed in which the text moves out of the way of the dragon is pretty incredible. As expected, many people are saying this is the end of CSS. So, let’s talk about it and address &lt;a href=&quot;https://chenglou.me/pretext/&quot;&gt;the demos provided that showcase some of the features&lt;/a&gt; in the lens of user experience.&lt;/p&gt;
&lt;h2 id=&quot;accordion&quot;&gt;Accordion&lt;/h2&gt;
&lt;p&gt;The first demo is of an accordion showcasing how the sections open and close smoothly without CSS hacks or measuring the DOM. This has been true for many years, we’ve wanted to animate from an intrinsic height for a long time. Well, we can do that now with pure CSS, in a few different ways.&lt;/p&gt;
&lt;p&gt;The first way was maybe a hack, which was to &lt;a href=&quot;https://nemzes.net/posts/animating-height-auto/&quot;&gt;animate the grid row&lt;/a&gt;. This was discovered 5 years ago. The difficulty with this approach is that it doesn’t work well with the native &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;The second way is to use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/interpolate-size&quot;&gt;&lt;code&gt;interpolate-size: allow-keywords&lt;/code&gt;&lt;/a&gt;. This allows you to animate between a &lt;code&gt;&amp;lt;length-percentage&amp;gt;&lt;/code&gt; value and an intrinsic size, &lt;code&gt;fit-content&lt;/code&gt;, or &lt;code&gt;max-content&lt;/code&gt;. This allows you to animate between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;auto&lt;/code&gt; for the height.&lt;/p&gt;
&lt;p&gt;And finally, we have &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/calc-size&quot;&gt;&lt;code&gt;calc-size()&lt;/code&gt;&lt;/a&gt; which is a function I recently used on the new &lt;a href=&quot;https://mode.place&quot;&gt;Mise en Mode&lt;/a&gt; website to get the &lt;code&gt;auto&lt;/code&gt; size as a number that can be used in other functions, such as &lt;code&gt;round()&lt;/code&gt;. This also calculates the intrinsic size of the content, so it can be used to animate from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;auto&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;As you might see when reviewing the MDN pages of these new CSS features, they don’t all have baseline availability yet. But do you really need JavaScript in order to animate the accordion? I’d argue you don’t, if you’re willing to put in a little extra effort learning what CSS has, and not ship some extra JavaScript for the sake of this.&lt;/p&gt;
&lt;h2 id=&quot;bubbles&quot;&gt;Bubbles&lt;/h2&gt;
&lt;p&gt;This is an interesting one. This is showcasing something called the “shrinkwrap” problem that is now more common than ever in a world of web chats. The problem is that CSS text wrapping isn’t efficient. The bubble that it leaves will have extra space not taken by text. What would be preferred is for the bubble to only be as big as the text needs to be. So without the shrinkwrap, each bubble is the same width. Meanwhile, with shrinkwrap, the bubbles match the size of the text leaving no extra space.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/bubble-wasted-pixels.png&quot; alt=&quot;Wasted pixels on text bubbles without shrinkwrap&quot;/&gt;&lt;/p&gt;
&lt;p&gt;There is a &lt;a href=&quot;https://kizu.dev/shrinkwrap-problem/&quot;&gt;CSS solution found by Roman Komarov&lt;/a&gt; to help with this problem which uses the new anchor positioning features. It also uses some pseudo element tricks to get the effect to behave. This is a pretty clever solution, but it is also pretty complex. It also doesn’t work in all browsers yet.&lt;/p&gt;
&lt;p&gt;The question is, do you want to bring in a JavaScript library just to get your text bubbles to shrinkwrap? Is this going to improve the user experience or is it an attention to detail that is not worth the cost of the extra JavaScript? I would argue the latter. The shrinkwrap problem is a problem, but it’s not a problem that is worth the cost of bringing in a JavaScript library to solve it. It’s a nice to have, but it’s not a must have.&lt;/p&gt;
&lt;h2 id=&quot;dynamic-layout&quot;&gt;Dynamic Layout&lt;/h2&gt;
&lt;p&gt;This looks like a magazine or editorial layout which has some images that are effecting the placement of the text. When I first saw this, I immediately thought of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/shape-outside&quot;&gt;&lt;code&gt;shape-outside&lt;/code&gt;&lt;/a&gt; property which allows you to wrap text around a shape. Honestly, I haven’t used this CSS property much but I do know it takes some setup in order for it to work. You’d need to have an alpha of the shape so that text can wrap around it. The object needs to be set as a &lt;code&gt;float&lt;/code&gt; in order for the wrapping to occur. Placing that element could be a pain, requiring additional margins to be added, most likely with &lt;code&gt;shape-margin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What CSS won’t accomplish is the rotation of the images. For example, setting &lt;code&gt;rotate: 45deg&lt;/code&gt; on the image doesn’t affect the text wrapping around it. In the pretext demo, rotating &lt;em&gt;does&lt;/em&gt; affect the text. Ultimately, I imagine you could maybe animate a very complex polygon of points to follow on an interaction. But the reality here is that if you want this effect, JavaScript is the way to go.&lt;/p&gt;
&lt;p&gt;But do you want this effect?&lt;/p&gt;
&lt;p&gt;Even without the animation, when text doesn’t consistently start at the same place, it can become tiring to read. This is why we don’t typically center body text. Our eye wants to align to the left (or right in RTL languages) and when it can’t, it can be a bit of a strain. The additional rotation that can occur would make catching the word you were trying to read difficult since all the lines are recalculating their position on every frame. This is a fun effect, but it’s not something that I would add to an article because it’ll distract from the content and make it harder to read.&lt;/p&gt;
&lt;h2 id=&quot;variable-typographic-ascii&quot;&gt;Variable Typographic ASCII&lt;/h2&gt;
&lt;p&gt;As you might imagine, this isn’t meant for you to read the text. This is instead a artistic effect that uses glyphs to create a visual piece of art. Since there isn’t anything here that is specifically UX related, I’ll refrain from commenting on it, past hoping that someone puts an &lt;code&gt;aria-hidden=&amp;quot;true&amp;quot;&lt;/code&gt; on it so that screen readers don’t try to read that unintelligible mess.&lt;/p&gt;
&lt;h2 id=&quot;editorial-engine&quot;&gt;Editorial Engine&lt;/h2&gt;
&lt;p&gt;Similar to the &lt;a href=&quot;#dynamic-layout&quot;&gt;dynamic layout&lt;/a&gt;, this is a layout that has some images that are effecting the placement of the text. The difficulty in reading the text is much more apparent here because the balls are constantly moving around making the location of every word change on every frame. As said earlier, not something I’d use for something I’d want someone to read.&lt;/p&gt;
&lt;h2 id=&quot;justification-comparison&quot;&gt;Justification Comparison&lt;/h2&gt;
&lt;p&gt;I have to admit this has some practical use. The justification algorithm in CSS leaves a lot to be desired. It doesn’t do a good job of balancing the space between words and letters, which leads to large gaps appearing within the text called rivers. The pretext demos show a better way of balancing the text such that the rivers are reduced, even having tighter algorithms available.&lt;/p&gt;
&lt;p&gt;Though, one thing you’ll need to be careful of is the introduction of hyphenation. CSS has a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/hyphens&quot;&gt;&lt;code&gt;hyphens&lt;/code&gt;&lt;/a&gt; property that allows you to loosely control when words are hyphenated. If you have a better justification algorithm, you might find that you need to hyphenate more often in order to get the best results. This is something to keep in mind when considering this approach as I did when typesetting the book &lt;a href=&quot;https://mode.place&quot;&gt;Mise en Mode&lt;/a&gt; and ultimately decided to avoid justifying the text because of the potential for rivers and the need for hyphenation. I wanted to avoid both of those things in order to provide the best reading experience possible. I kept the content as the primary focus in a way that was familiar without stressing over the details of justification. Curating every small hyphen is very tedious. I personally rather keep the words intact and not have full justification.&lt;/p&gt;
&lt;h2 id=&quot;rich-text&quot;&gt;Rich Text&lt;/h2&gt;
&lt;p&gt;Again, I think this does have some practical use. &lt;a href=&quot;/posts/book-authoring-backstage/&quot;&gt;I’ve recently written about the difficulty in wrapping inline code accurately in CSS.&lt;/a&gt; In this demo, the inline code does wrap in a reasonable way but I’ll also say there’s configurations that make the wrapping a bit too eager.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/eager-wrapping.png&quot; alt=&quot;Wrapping has occurred too soon breaking the word docs&quot;/&gt;&lt;/p&gt;
&lt;p&gt;It might make some sense to add some &lt;code&gt;&amp;lt;wbr&amp;gt;&lt;/code&gt; spaces into the code at the right places. This could be done in a preprocessing step instead of on the client where the &lt;code&gt;pretext&lt;/code&gt; project needs to run.&lt;/p&gt;
&lt;h2 id=&quot;masonry&quot;&gt;Masonry&lt;/h2&gt;
&lt;p&gt;Ah yes, the layout everyone is waiting for. While &lt;code&gt;display: grid-lanes&lt;/code&gt; isn’t the property value we might have been expecting, the industry has been asking for a masonry layout for a long time. The pretext demo shows how to achieve this layout with JavaScript.&lt;/p&gt;
&lt;p&gt;While we’ve known that we’ve needed JavaScript to have dynamic masonry layouts &lt;a href=&quot;https://masonry.desandro.com/&quot;&gt;for a long time&lt;/a&gt;, there’s something you need to consider when implementing them; reading order.&lt;/p&gt;
&lt;p&gt;When you look at a masonry layout, what is the order of the boxes meant to be read in? Is it top to bottom, left to right? Or is it left to right, top to bottom? Maybe it changes due to one box being longer than another. If you inspect the demo you’ll see that some of the items are inserted in places that aren’t in any clear order. It is most likely using a dense packing algorithm to efficiently use the available space, but this isn’t necessarily how the items are meant to be consumed.&lt;/p&gt;
&lt;p&gt;The new &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/reading-flow&quot;&gt;&lt;code&gt;reading-flow&lt;/code&gt;&lt;/a&gt; property in CSS is meant to help suggest how these items are meant to be read. I’ll say with the amount of consideration the &lt;a href=&quot;https://www.w3.org/TR/css-grid-3/&quot;&gt;CSSWG has put into &lt;code&gt;display: grid-lanes&lt;/code&gt;&lt;/a&gt;, I’d trust that they’ve thought more about how masonry layouts should be managed more than “whatever fits best”.&lt;/p&gt;
&lt;h2 id=&quot;what-is-pretext-for&quot;&gt;What is pretext for?&lt;/h2&gt;
&lt;p&gt;It’s for people who want more control over how text flows; either for artistic purposes or for typesetting designers who crave the tools traditional print design has had for a long time. Just remember &lt;strong&gt;the web is not print&lt;/strong&gt;. Text has never animated on a magazine page, nor has that magazine page needed to download additional code in order for the layout to behave. These are different mediums even if we’ve made analogs to them over the years.&lt;/p&gt;
&lt;p&gt;I’m certainly not saying to avoid using the library, I think it’s fun and does have some practical use cases. But you should first consider your users and whether the features of this library are worth the cost of the extra JavaScript and the potential decrease in user experience. If you do decide to use it, make sure to use it in a way that enhances the content and doesn’t distract from it.&lt;/p&gt;
&lt;p&gt;This certainly isn’t the end of CSS, it’s showing what’s next.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>typography</category><category>css</category></item><item><title>Book Authoring Backstage</title><link>https://blog.damato.design/posts/book-authoring-backstage</link><guid isPermaLink="true">https://blog.damato.design/posts/book-authoring-backstage</guid><description>Some behind the scenes notes about book writing.</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;By the time you are reading this, my several-year-long journey in book writing has neared its end. &lt;a href=&quot;https://mode.place&quot;&gt;My first book, Mise en Mode, is now available for purchase.&lt;/a&gt; Now that it’s complete, I wanted to document some of the process so that you might consider venturing out into the great unknown of self-publishing a little better than I did.&lt;/p&gt;
&lt;h2 id=&quot;how-it-started&quot;&gt;How it started&lt;/h2&gt;
&lt;p&gt;After &lt;a href=&quot;https://conffab.com/presentation/mise-en-mode/?gl=6qwtmtB93YWT&quot;&gt;speaking on the subject at Clarity in 2023&lt;/a&gt;, the topic generated a great deal of interest. I received lots of questions and people who wanted to know more. As per usual, I sat down and started writing another blog article here. But after looking at the reading time showing 15 minutes, not being anywhere near done, I felt it was time to go to the &lt;em&gt;next level&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I’ve always been interested in writing a book. I have a few books on writing books. Though I never had a topic that I felt passionate and knowledgeable enough to write about. I know they say that just because someone has written something before, there’s no reason not to write about it again, but I wanted to be unique with whatever my book was about. Mise en Mode was most certainly that opportunity.&lt;/p&gt;
&lt;h2 id=&quot;where-to-write&quot;&gt;Where to write&lt;/h2&gt;
&lt;p&gt;The next hurdle was &lt;em&gt;where&lt;/em&gt; I was supposed to put these words. I did some research here, and even spoke with Ethan Marcotte early on about his process. My big worry was, “if I start writing in X, will that make it easy to turn into a physical book?” The answer was one of the hardest parts of the process.&lt;/p&gt;
&lt;p&gt;I first started writing in &lt;a href=&quot;https://reedsy.com/&quot;&gt;reedsy&lt;/a&gt;. Its main selling point is that you will absolutely get a printed book using their platform. Additionally, it’s a web-based editor so I can write anywhere. I actually tried to use reedsy &lt;em&gt;twice&lt;/em&gt;, thinking that the shortcomings could be overlooked. The reality is that reedsy is not suited for technical books. It can’t support inline code and to speak about design tokens, there’s a lot of inline code. I figured if you’re writing something non-technical, this would be a good place to try.&lt;/p&gt;
&lt;p&gt;So from there, I searched for something that was more suited for technical writing. I saw some reddit posts that claimed the publishing houses might expect &lt;a href=&quot;https://asciidoc.org/&quot;&gt;AsciiDoc&lt;/a&gt; as a format because they already have templates to render that format into a book. From the look of the syntax it was markdown-like which was good. I preferred to write in markdown because I’ve been using it for years and it streamlines the content management process. I could also write and version control using GitHub so I could even edit in GitHub directly. The syntax isn’t really markdown, it’s a bit different but not too far from what I was used to writing.&lt;/p&gt;
&lt;p&gt;It’s been a while and I don’t remember the specifics but I ended up having problems rendering the final book using AsciiDoc. I know you need to install a few things in order to get it working, which I don’t really like. I think the biggest problem was customizing the final output to something that looked like a real book.&lt;/p&gt;
&lt;p&gt;That caused me to go out and look for other solutions. I’ll also say there’s a lot of red herrings out there. For example, the first line of &lt;a href=&quot;https://rust-lang.github.io/mdBook/&quot;&gt;mdbook&lt;/a&gt; says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;mdBook is a command line tool to create books with Markdown.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But when you read the next line, it’s clear this isn’t what I want:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is ideal for creating product or API documentation, tutorials, course materials or anything that requires a clean, easily navigable and customizable presentation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It should be called mdDoc or mdWebpage.&lt;/p&gt;
&lt;p&gt;I eventually found &lt;a href=&quot;https://quarto.org/&quot;&gt;Quarto&lt;/a&gt; which has a syntax even closer to markdown. I even tried rendering what I had as a book and at first glance it seemed like everything worked well. I ended up writing the entire rest of the book in Quarto.&lt;/p&gt;
&lt;p&gt;Then, came the rendering.&lt;/p&gt;
&lt;p&gt;Even though I had sent a first pass through, it was where you look at the details. What I found was snag after snag. Quarto uses LaTeX and pandoc to render documents. LaTeX for the formatting, and pandoc to convert to a specified output. If you touch nothing, this works well enough. But if you need anything special, you are in for a challenge.&lt;/p&gt;
&lt;p&gt;One of the things that really caused me to throw out this toolchain was line wrapping. The token names in the book are long. For example &lt;code&gt;$action_primary_backgroundColor&lt;/code&gt; and because of this I expected the inline code to wrap when necessary. Otherwise, you’d get huge distracting gaps in the text. You might even be seeing this awful lack of wrapping in this very post! I’ll fix it here, eventually…&lt;/p&gt;
&lt;p&gt;However, for a printed book where I could curate how it looked precisely, this was unacceptable. However, no matter what I tried, I couldn’t get LaTeX to make these wrap. There were plenty of other problems like when to make pages break. I’d have code blocks and callouts broken between pages. I couldn’t believe how hard this was, or how this wasn’t just something built into the system. Why would it be acceptable for a code block that isn’t larger than a page to split between pages? I said to myself, I know exactly how to do this in CSS.&lt;/p&gt;
&lt;p&gt;One last attempt was to try and make this work using Adobe InDesign. My wife worked in publishing before and was familiar with the features of the program. However, what made this a non-starter was the restrictions on the callouts. Because the callouts are in their own colored boxes, they can’t be given styles like the normal paragraphs. My wife was trying to hack together something to make them look good enough, but at this point I didn’t want good enough. I wanted it to look like I expected it to look and I wanted it to be easy so if I needed to make an edit, it wouldn’t break the entire layout.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;At this point in the timeline, my book didn’t have an editor yet. I was shopping it around to different publishers, and when I did find a publisher I didn’t like that the rights were handed over. That’s why I ultimately chose to self-publish but also why it was important to have a process where I could make changes easily.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;css-to-the-rescue&quot;&gt;CSS to the rescue&lt;/h2&gt;
&lt;p&gt;Toward the end of my frustrations with book layout, I couldn’t stop but think to myself “Why is this so hard? I could do this in CSS in minutes!” That’s when I went out to see if anyone actually did do this. That’s when I found &lt;a href=&quot;https://iangmcdowell.com/blog/posts/laying-out-a-book-with-css/&quot;&gt;this post by Ian McDowell&lt;/a&gt; where he outlines how he used CSS to layout his print book. He even shows the result at the end which was very inspiring.&lt;/p&gt;
&lt;p&gt;So, that’s exactly what I did. While I’m normally an &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; fanboy, I decided to use &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;11ty&lt;/a&gt; for this because all I needed to do was convert markdown to HTML and 11ty is made for that. Then I could just write CSS as I normally would, hit &lt;code&gt;Ctrl+P&lt;/code&gt; on my keyboard and save it as a PDF. There’s actually not much more to it than that, but I’ll go over the items that are unusual.&lt;/p&gt;
&lt;p&gt;In my book I set up the following values:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --page-size&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 6&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 9&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --page-margin&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; .75&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --inner-margin&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--page-margin) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.5)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --runner-margin&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--page-margin) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 2)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--page-size&lt;/code&gt; is used in the &lt;code&gt;@page&lt;/code&gt; declaration directly, along with the &lt;code&gt;--page-margin&lt;/code&gt; to go around each page:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    size&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--page-size)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    margin&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--page-margin)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    counter-increment&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That counter increment property is described in &lt;a href=&quot;/posts/late-start/&quot;&gt;an earlier post that helps create page numbers&lt;/a&gt;. Next, we need to increase the inner margin on the correct pages.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; :left&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    margin-right&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--inner-margin)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;bottom-left-corner&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        font&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--font-caption)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        content&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; counter&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; false-start)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        margin-block-end&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--runner-margin)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; :right&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    margin-left&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--inner-margin)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;bottom-right-corner&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        font&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--font-caption)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        content&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; counter&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; false-start)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        margin-block-end&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--runner-margin)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The runner is the bottom of the page and this is a bit larger to make room for the page numbers. Finally a few other items to help with printing:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    -webkit-print-color-adjust&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; exact &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;!important&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    print-color-adjust&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; exact &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;!important&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/print-color-adjust&quot;&gt;&lt;code&gt;print-color-adjust: exact&lt;/code&gt;&lt;/a&gt; is used to tell the browser that these styles are made for print and to not make any opinionated changes to the page. Without it, the print rendering could look unexpected by removing background colors for example.&lt;/p&gt;
&lt;p&gt;From there, we have a few additional properties that help. For example, here’s all of the items that I want to avoid a page break:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;ol&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; ul&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; table&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; figure&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; pre&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; blockquote&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;){&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    break-inside&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; avoid&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For headings, I have slightly different rule.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; h2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    break-before&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; right&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; h4&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    break-before&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; avoid&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For &lt;code&gt;h1&lt;/code&gt; (part titles) and &lt;code&gt;h2&lt;/code&gt; (chapter titles), I want to make sure that they always start on a right-hand page. That’s what &lt;code&gt;break-before: right&lt;/code&gt; is &lt;em&gt;supposed to do&lt;/em&gt; in theory. In practice, it doesn’t work because no browser has implemented it. Instead, I created a blank page element that I manually insert to get those pages in the correct place.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;h3&lt;/code&gt; and &lt;code&gt;h4&lt;/code&gt; elements, I want to make sure that the following paragraph is connected to those headings. Otherwise, it would be weird if the heading was at the end of a page with its first paragraph on another page.&lt;/p&gt;
&lt;p&gt;There’s &lt;a href=&quot;https://www.smashingmagazine.com/2015/01/designing-for-print-with-css/&quot;&gt;a lot more to the CSS print specification&lt;/a&gt; that browsers haven’t implemented. &lt;a href=&quot;https://www.princexml.com/&quot;&gt;PrinceXML&lt;/a&gt; claims that its main feature is to implement these features and more. I looked into it after finishing the book, but it looks like it might be more involved than I was hoping. It wasn’t clear how to get it working after downloading the zip for me.&lt;/p&gt;
&lt;p&gt;But after that, I had a process to get a real ready-to-print PDF of my book. Next, is to make it real.&lt;/p&gt;
&lt;h2 id=&quot;publishing&quot;&gt;Publishing&lt;/h2&gt;
&lt;p&gt;I did a lot of research into self-publishing and ultimately went with &lt;a href=&quot;https://www.ingramspark.com/&quot;&gt;Ingram Spark&lt;/a&gt;. Most of the complaints were around customer service being slow, but the benefits of worldwide distribution and not being locked into a single Amazonian ecosystem was very enticing.&lt;/p&gt;
&lt;p&gt;Before this I bought ISBN numbers from &lt;a href=&quot;https://www.myidentifiers.com/&quot;&gt;Bowker&lt;/a&gt; because then you are able to have more flexibility than if a publisher gives you a free one.&lt;/p&gt;
&lt;p&gt;I’ll say that the process was mostly painless. There’s some tools at Ingram Spark that help you prepare your book. The cover template is a little tricky. For my hardcover it wasn’t clear how the template would be used. After getting the first book printed it was more clear that some of the lines were meant to indicate how the cover folds around the book.&lt;/p&gt;
&lt;p&gt;We originally made the cover in Adobe Illustrator but then cancelled our subscription because the cost was getting out of hand. Luckily &lt;a href=&quot;https://www.affinity.studio/&quot;&gt;Affinity&lt;/a&gt; is free and works just as well. There was a bit of a learning curve but we got the results we needed for the second round of printing. It was close enough such that when I submitted the cover design a third time with a small tweak, I was confident enough to enable the book to be sold without buying myself another copy.&lt;/p&gt;
&lt;p&gt;When you’re adding your book to your account, Ingram Spark asks you some questions about the purchase price. In this there’s areas where you need to set the wholesale discount. This is where I got tripped up. Basically, if someone buys your book direct from Ingram Spark, your cut is much higher than if they were to buy it from a middleman like Amazon. This means that it’s better for people to buy it direct from “you” (Ingram Spark). What they don’t tell you is that when Ingram Spark says they have the best distribution network, they mean to resellers, not themselves. I didn’t know this, so when I released my book, I found out it was only available to the US and UK directly through Ingram Spark.&lt;/p&gt;
&lt;h2 id=&quot;future&quot;&gt;Future&lt;/h2&gt;
&lt;p&gt;I’ve already started the process of getting the e-book available. It’s in review inside Ingram Spark and I’m planning to do a joint release making the e-book available at the same time that I enable worldwide distribution of the hardcover. I don’t know how long it’ll take for that to reach resellers but keep an eye out on my socials because I’ll be checking periodically and eventually updating the website with links that I get to the book.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>community</category></item><item><title>Thinking Full Stack</title><link>https://blog.damato.design/posts/thinking-full-stack</link><guid isPermaLink="true">https://blog.damato.design/posts/thinking-full-stack</guid><description>Delegate design decisions to the people who own them</description><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A large part of the role for design system maintainers is to make things for your peers. This comes with having empathy for your organization and the people who compose it. Many organizations are composed of engineers that are jacks-of-all-trades. The idea of having a developer that can manage the front &lt;em&gt;and&lt;/em&gt; the back-end seems very appealing to organizations. Especially since CSS is so easy, we don’t even need to test for it! 🫠&lt;/p&gt;
&lt;p&gt;Enter the &lt;em&gt;full-stack engineer&lt;/em&gt;. Where you work probably has a lot of them, some companies might even highlight “we’re all full-stack here” with varying degrees of prowess. As design system maintainers we are often adjusting our strategies to fill in the gaps of full-stack knowledge. The CSS skill that wasn’t assessed in the interview? Don’t worry, we have a design system for that.&lt;/p&gt;
&lt;p&gt;In this post, I want to explain something to full-stack engineers that is often confusing: why can’t I just declare that the button is blue?&lt;/p&gt;
&lt;h2 id=&quot;the-button-is-blue&quot;&gt;The button is blue&lt;/h2&gt;
&lt;p&gt;You get a mockup from your designer for a shiny new button. You look at the carefully crafted specifications and start coding. It’s just a button, so no worries about complex structure. But you know there’s probably a lot of states, so you’ll definitely be writing some CSS. If you’re lucky, your team is leveraging Tailwind which makes it easy to declare all of the styles right there in the structure. You don’t need to think of a container name for the styles, you can just say &lt;code&gt;className=&amp;#39;bg-blue-500&amp;#39;&lt;/code&gt; and the button is now blue. Perfect!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blue-button-first-color.png&quot; alt=&quot;Developer making a button for the first time&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Some time passes and changes happen in the design. The button isn’t performing well so we need to change it. You get a new design and see it is just some color changes. You get back into those Tailwind classes and start making adjustments. Toggling states in Storybook, updating visual regression tests, and anything else that was affected by the update. It’s not as time consuming as when you first created the button, but it’s not a simple find and replace. You create the PR, get the sign off from your other team members and get the change in the deploy pipeline. Back to your regularly scheduled tickets.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blue-button-pipeline.png&quot; alt=&quot;Developer making changes to the button for the first time&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Some more time passes and the head of marketing pings your team about a special promotion that we need to run for 2 weeks, which is cobranded with another partner. We need a few new pages made that have buttons but they need to look different than the other ones because they need to show this new cobranded presentation. It takes too much time to make a wholly new button, so you want to make some quick overrides to the original one for this special purpose. Again, you go through the motions of updating classnames, maybe even making a special “promo” variant because this isn’t the first time you gotten this kind of request. Now the button’s variants are getting numerous: primary, secondary, critical, info, highlight, promo. Each of them has slight differences, some of which we don’t even know if they are still being used. You get the change in the pipeline, and it’s ready for use in the promotional pages. The promotion was a huge success, and now we have to tear it down. It doesn’t make sense to go into the button and remove the code, I have more important things to take care of. We’ll write a ticket and put it in the backlog.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blue-button-variants.png&quot; alt=&quot;Developer maintaining variants for button&quot;/&gt;&lt;/p&gt;
&lt;p&gt;A new designer comes on board and is in charge of a fresh new look but they aren’t familiar with the current state of affairs. They’re exploring things in their design tool, and aren’t organized yet. They want to see how changes they make would look in real applications. You know we don’t really have a good way of doing that. Each design change has to be coded, tested, reviewed, and deployed. The process takes a while so it’s really hard to iterate on how a design decision could affect the current experience. All you could recommend is to try things in design and then you’ll make changes in the code to match. Luckily, you have some auto deployed Storybook that can give an idea of what things might look like, but we’ll never really be sure until it’s out in the wild. Even then, we might find oversights that we’ll require a rollback and re-release. All of it taking time away from more important infrastructure work that you were actually assessed on in your original interview.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blue-button-new-designer.png&quot; alt=&quot;Developer working with a new designer&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Wouldn’t it be better if the person who wanted to make these little design changes could just do it themselves?&lt;/p&gt;
&lt;h2 id=&quot;in-the-code&quot;&gt;In the code&lt;/h2&gt;
&lt;p&gt;Let’s say you have the following JSX code:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({ variant&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;props }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;props } &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;className&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;clsx&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;        &amp;#39;bg-blue-500&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; variant &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;primary&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }) }&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you might say, toggling classes with &lt;a href=&quot;https://www.npmjs.com/package/clsx&quot;&gt;&lt;code&gt;clsx&lt;/code&gt;&lt;/a&gt; is old news, we use &lt;a href=&quot;https://cva.style&quot;&gt;&lt;code&gt;cva&lt;/code&gt;&lt;/a&gt; now. Ok, here’s the same thing with &lt;code&gt;cva&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; cva&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({ variants&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    variant&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { primary&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;bg-blue-500&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;} });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Under the hood, providing the concept of &lt;code&gt;primary&lt;/code&gt; is still applying a declarative class that directly represents the blue background for the button.&lt;/p&gt;
&lt;p&gt;But maybe you have a more sophisticated system. Maybe you have a global palette that’s type-safe and ensures only the colors that are meant to be applied can be used. Here’s how this might look using &lt;a href=&quot;https://panda-css.com/&quot;&gt;Panda CSS&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({ variant&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;props }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;props}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;            className&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;css&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({ bg&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;blue.500&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; }) } /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In all of these cases, the person coding the button is ultimately in charge of the way it is presented. Sure, someone else told them to use &lt;code&gt;bg-blue-500&lt;/code&gt;, but that will never make it to production and get in front of users without the developer interpreting that change in code. Even though they aren’t the person who made the decision about what color this should be, it is the developer’s responsibility to maintain the application of this color to the element. Every little decision that a designer, marketer, or other stakeholder might want is gated behind the developer.&lt;/p&gt;
&lt;p&gt;In my view, the full-stack developer is not a stakeholder in the final presentation of the button. So it doesn’t make sense that they should have the responsibility of maintaining how it looks. Instead, we should provide some proxy that allows the decision from a stakeholder to be made by the stakeholder. Delegating the responsibility about the color of the button to the people who actually care what the button looks like. That leaves the developer open to tackle the engineering tasks that they were trained to do.&lt;/p&gt;
&lt;p&gt;So, how could we let a stakeholder apply the color they want to these buttons? We’d need some way of allowing their decision to be restricted to only design properties, and ensure nothing else is affected.  We can’t just make this another prop. Props are used by developers so other non-developers won’t use them. We need a list of all the design decisions that we expect stakeholders want to change, and allow them to assign values to those in some curation exercise. Then our components could look up the values they assigned and apply those assigned values to the associated properties.&lt;/p&gt;
&lt;h2 id=&quot;new-naming&quot;&gt;New naming&lt;/h2&gt;
&lt;p&gt;Ok cool, so let’s try using what we have to make this. First we set up some list that people can use to change the values that are assigned. Let’s assume we have some UI that allows stakeholders to make updates to it. It could be a simple spreadsheet or a more robust interface:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    &amp;quot;blue&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;        &amp;quot;500&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;#006be6&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    &amp;quot;orange&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;        &amp;quot;500&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;#f97316&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since our components are already using this, we don’t need to change anything there. Sounds good! Except until the button needs to be orange. Since the button has &lt;code&gt;blue.500&lt;/code&gt; assigned, we’d need to go back into the button to change it to &lt;code&gt;orange.500&lt;/code&gt;. That’s not what we want because going back into the button’s code is a task for the engineer, and only the engineer. We need some way of having the decision to change from blue to orange for the button to be represented here.&lt;/p&gt;
&lt;p&gt;Why not have the system describe the element and property being affected? Something like &lt;code&gt;button.primary.bg&lt;/code&gt; and &lt;code&gt;button.primary.text&lt;/code&gt;. That way the person who is trying to update the color knows what it’ll affect, and the person assigning that variable knows where to place it. They don’t even need a design mockup to figure out where this goes. The place where it goes is right in the name. It makes it clear to everyone on the team what this thing is meant to represent. Let’s update our things:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    &amp;quot;button&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;        &amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;            &amp;quot;bg&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;#006be6&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;            &amp;quot;text&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;#fefefe&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be represented in some UI that makes it clear these are the colors for the primary button’s background and text so that a stakeholder can change it whenever they like. Now, the developer just needs to read from these new values:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({ variant&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;props }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;props} &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;            className&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;css&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({ bg&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;button.primary.bg&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; }) } /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What we’ve begun to introduce is called a “semantic” naming strategy. Using this, we describe what is going to be affected so that someone else can provide the value in the pipeline. This relieves the engineer from looking up or maintaining the presentation of the button and make it an exercise for folks that are more responsible for those decisions.&lt;/p&gt;
&lt;p&gt;Those visual regression tests? Now those tests happen before the list is published. We can know how the colors are meant to be used before they hit the components. We can check for contrast, create an alpha set for testing in real parts of the application. Separating the concerns balances the responsibility of the team.&lt;/p&gt;
&lt;h2 id=&quot;trust-the-process&quot;&gt;Trust the process&lt;/h2&gt;
&lt;p&gt;When a full-stack engineer first starts using a semantic naming strategy, they might be uncomfortable because the decision about what color the button is no longer belongs to them. It is delegated outside to another person. What if they choose the wrong color? Why does the button suddenly look weird? People are going to think it’s my fault and I did something wrong. These are all valid thoughts to have when encountering a new paradigm. New ways of approaching something that has been the same way over years is concerning.&lt;/p&gt;
&lt;p&gt;You must be thinking, why doesn’t Tailwind’s default approach lean on semantic naming? The answer is because the scenarios I’ve described above don’t happen often, but when they do the pain is felt. When you first put &lt;code&gt;bg-blue-500&lt;/code&gt; in your code, you don’t think about the future. Of course not, you are thinking about pushing this code right now. Changing this code is your future self’s problem. Tailwind isn’t preparing you for the future, it’s preparing you to ship right now and that’s often what matters. However, the reality is that without a semantic layer, it can’t easily account for iterations, experimentation, and stakeholder requests. Maybe you don’t have these, so you’ll never need to revisit the button.&lt;/p&gt;
&lt;p&gt;But when you do receive that ticket to change the button’s background color, the answer isn’t to dig back into the code. It’s to make sure the right person can make that decision without ever needing to ping you. That’s empowering your team to express their creativity while you get back to your own expertise.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>naming</category><category>components</category><category>organization</category></item><item><title>Avoiding z-index</title><link>https://blog.damato.design/posts/avoiding-z-index</link><guid isPermaLink="true">https://blog.damato.design/posts/avoiding-z-index</guid><description>My thing is more important than your thing</description><pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;From the days of working with design tools like Photoshop, layering compositions in 3D space on a computer has been part of the job. Managing what appears on top can be dozens of elements, maybe even groups of elements, and carefully curating the order of the layers is often a necessary unit of work.&lt;/p&gt;
&lt;p&gt;As you can expect, this sort of exercise has translated to the web, and traditionally we think the &lt;code&gt;z-index&lt;/code&gt; property is meant to manage this ordering in a similar way to how layers would work in a design tool. When you are working alone on your own project, this is reasonable because you are in control of the elements, the order, and the composition as a whole. You can decide if the &lt;code&gt;z-index&lt;/code&gt; you added yesterday isn’t good enough today and make modifications. You can tweak this part here, and make a small change there to get everything just right. In fact, I use &lt;code&gt;z-index&lt;/code&gt; right here on the blog in a few specific places to get things looking just right artistically.&lt;/p&gt;
&lt;p&gt;But for larger teams where elements are coming together from all across the organization to be composed into a single experience, trying to manage &lt;code&gt;z-index&lt;/code&gt; is a political battlefield. It’s not possible to just make a &lt;code&gt;--dropdown-elevation&lt;/code&gt; variable, because that dropdown might be on another surface that itself has an elevation higher than the dropdown.&lt;/p&gt;
&lt;p&gt;Popular public design systems like &lt;a href=&quot;https://polaris-react.shopify.com/tokens/z-index&quot;&gt;Shopify Polaris&lt;/a&gt; include &lt;code&gt;z-index&lt;/code&gt; tokens which don’t mention where the token is meant to be used, we just know they exist.&lt;/p&gt;
&lt;p&gt;There are certainly attempts at making sense of this. &lt;a href=&quot;https://css-tricks.com/the-value-of-z-index/&quot;&gt;A recent article on CSS-Tricks&lt;/a&gt; suggest naming the elements where they are expected to appear. Semantically, this makes sense on the surface. But as mentioned earlier, it’s very possible that a flyout that is normally underneath a modal is meant to later be on top of it. And it would be inappropriate to make an entirely separate component meant for “modal dropdowns”. Maybe it’s OK to set your own &lt;code&gt;z-index&lt;/code&gt;? Well, how do you know it isn’t conflicting with another element that will appear later in the lifecycle. You’re essentially playing whack-a-mole, goldilocking the perfect &lt;code&gt;z-index&lt;/code&gt; value, tokens or no tokens.&lt;/p&gt;
&lt;p&gt;But what if you didn’t need &lt;code&gt;z-index&lt;/code&gt; at all? There’s two approaches I want to highlight and both related to a very simple concept. &lt;strong&gt;Elements are visible in the order they appear in the DOM.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;top-layer&quot;&gt;Top layer&lt;/h2&gt;
&lt;p&gt;For what purpose do we set elements in 3D space on the web at all? Certainly, we can stylistically show something is a little bit raised for some personality but when we are creating flyouts and popovers, why are we using them. Overall, these are meant for focused attention. The user was currently navigating the general interface but needs to either focus on something further to continue. Sometimes that’s to choose an option, or maybe be notified of something. This means that there’s a hierarchy to what is being presented. This thing is currently important, then this thing is important, and so on. So this means as we introduce elements to the user, this is conveying a sense of importance. The most recent thing that is added, is the thing the user needs to see more than any other.&lt;/p&gt;
&lt;p&gt;This is how the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Top_layer&quot;&gt;top layer&lt;/a&gt; works in HTML. It is a separate layer from the rest of the DOM where anything placed within it will be on top anything that would be placed in the normal DOM. No amount of &lt;code&gt;z-index&lt;/code&gt; can place elements higher than the top layer. Additionally, z-index doesn’t work in the top layer. So your question might be, how do I provide the correct order of elements? The order of the elements is how they are added to the top layer. That way, if you want some messaging to be the newly most important thing, you simply append it to the top layer.&lt;/p&gt;
&lt;p&gt;Elements that can apepar in the top layer are restricted to the following &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Top_layer&quot;&gt;according to MDN&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fullscreen elements, i.e., elements that have been caused to display in fullscreen mode by a successful &lt;code&gt;Element.requestFullscreen()&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; elements displayed as a modal via a successful &lt;code&gt;HTMLDialogElement.showModal()&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;Popover elements shown via a successful &lt;code&gt;HTMLElement.showPopover()&lt;/code&gt; call.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;It seems you can also declaratively get popover elements into the top layer. There’s an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover&quot;&gt;example at MDN&lt;/a&gt; using the &lt;code&gt;popover&lt;/code&gt; attribute which successfully renders into the top layer.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;Now, it’s more clear what is supposed to be on top. You can see all the elements and their visual order in devtools. Here’s an gif from the &lt;a href=&quot;https://developer.chrome.com/blog/what-is-the-top-layer&quot;&gt;Chrome for Developers article on the top layer&lt;/a&gt; showing how to inspect these elements.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/devtools-top-layer.gif&quot; alt=&quot;Devtools top layer inspection&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Devon Govett, one of the engineers behind Adobe Spectrum, &lt;a href=&quot;https://github.com/adobe/react-spectrum/discussions/9696#discussioncomment-15942257&quot;&gt;recently described some of the drawbacks&lt;/a&gt; about using the top layer. I’ve made a response to the comments about &lt;code&gt;&amp;lt;dialog/&amp;gt;&lt;/code&gt; in &lt;a href=&quot;https://www.youtube.com/watch?v=-blEWN_jM64&quot;&gt;a recent Wireframe episode&lt;/a&gt;. But it’s important to reiterate that you &lt;em&gt;do&lt;/em&gt; have control of the order, and I’d argue better control than spraying a new &lt;code&gt;z-index&lt;/code&gt; number and praying it doesn’t conflict with something else.&lt;/p&gt;
&lt;h2 id=&quot;grid-positioning&quot;&gt;Grid positioning&lt;/h2&gt;
&lt;p&gt;The other common reason why &lt;code&gt;z-index&lt;/code&gt; is used is to compositionally place elements on top of each other. This was very common when we used to use the &lt;a href=&quot;https://css-tricks.com/aspect-ratio-boxes/&quot;&gt;aspect ratio hack&lt;/a&gt; years ago for video. In this hack we’d need to use a padding value as a percentage which is based on the width of the element to maintain the aspect ratio of the box. Then we’d break the inner container out of the flow, and make it fill the parent’s dimensions.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.aspect-ratio-box&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  height&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  overflow&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; hidden&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  padding-top&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(100&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; (16&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;9)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; relative&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.aspect-ratio-box-inside&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  position&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; absolute&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  inset&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These days, if we needed a similar effect, we can just use &lt;code&gt;aspect-ratio: 16/9&lt;/code&gt; on the parent, but having a child fill a container is still important today. And it is commonly done using this combination of &lt;code&gt;position&lt;/code&gt; values.&lt;/p&gt;
&lt;p&gt;Using position creates a new &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Positioned_layout/Stacking_context&quot;&gt;stacking context&lt;/a&gt; which you probably don’t want and may have not even known was happening. While it might be harmless to use in one place, when you start positioning many shared elements, you might find putting a &lt;code&gt;z-index&lt;/code&gt; on one of them causes something else that was positioned to conflict. Understanding stacking contexts is very important when working with positioned elements, and there’s a &lt;a href=&quot;https://www.joshwcomeau.com/css/stacking-contexts/&quot;&gt;great article by Josh Comeau&lt;/a&gt; about them.&lt;/p&gt;
&lt;p&gt;But you can avoid creating a stacking context altogether using CSS grid! Assuming a similar HTML setup for the aspect ratio hack, here’s what that looks like with grid. I’ve removed the padding hack since we don’t need that any longer with modern CSS.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.aspect-ratio-box&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  aspect-ratio&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 16/9&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  display&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; grid&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.aspect-ratio-box-inside&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  grid-area&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1 / 1 / -1 / -1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Untitled&quot; src=&quot;https://codepen.io/fauxserious/embed/yyagwjq?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot;&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/yyagwjq&quot;&gt;
Untitled&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;Any time you want to have an element match the dimensions of its parent, this is the technique that you should think of first. This approach avoids using &lt;code&gt;position&lt;/code&gt; and works with the order in which elements are provided.&lt;/p&gt;
&lt;p&gt;You can also place elements relative to the container. For example, maybe you’d like to place a close button at the top corner.&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Grid positioning banner&quot; src=&quot;https://codepen.io/fauxserious/embed/emdgXLx?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot;&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/emdgXLx&quot;&gt;
Grid positioning banner&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;I wouldn’t recommend this specific approach for handling a close button because &lt;a href=&quot;/posts/close-thy-enemy/&quot;&gt;the composition’s elements might conflict with each other&lt;/a&gt;. You should see the problem with this if you resize the example, heading will appear over the button. But it should give you some ideas of what else you might use CSS grid positioning for. Want the button on top, reorder the elements in the HTML.&lt;/p&gt;
&lt;p&gt;When we use and promote techniques that avoid pitfalls, we produce more robust applications and consistent systems of composition. Sure, CSS gives us a lot of tools, but it’s up to us to use them in thoughtful ways. Reaching for &lt;code&gt;z-index&lt;/code&gt; should be a last resort when everything else doesn’t work. There’s better ways in a modern web, we just need to take a moment while our chatbots are working to explore novel solutions on our own.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category><category>components</category><category>tokens</category><category>organization</category></item><item><title>People&apos;s Primitives</title><link>https://blog.damato.design/posts/peoples-primitives</link><guid isPermaLink="true">https://blog.damato.design/posts/peoples-primitives</guid><description>You crave them but you don&apos;t need them</description><pubDate>Tue, 03 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I tend to take a minimalist approach when it comes to design systems. One of the reasons for this is the empathy I have for maintainers of these systems. The responsibility of supporting a group of creatives while not being overwhelmed by the number of resources they need is a challenge. However, this doesn’t mean I don’t have empathy for the people looking to express themselves. It’s why I am so proud of the &lt;a href=&quot;https://mode.place&quot;&gt;Mise en Mode&lt;/a&gt; approach; that it allows for &lt;em&gt;infinite expressions&lt;/em&gt; in ways other systems would fail when pressed, and fail in different ways.&lt;/p&gt;
&lt;p&gt;One major drawback to Mise en Mode is being &lt;em&gt;new&lt;/em&gt;. Not just new in terms of being recent, but also in terms of thinking. It’s the same sort of paradigm shift that took responsive design years to change the way of working, and even more related to the need for semantic tokens to support both light and dark expressions. It’s just hard to think differently for many.&lt;/p&gt;
&lt;p&gt;So when I say, you—&lt;em&gt;collectively&lt;/em&gt;—need primitives but you—&lt;em&gt;yourself&lt;/em&gt;—don’t need primitives, it’s just a different way of thinking.&lt;/p&gt;
&lt;h2 id=&quot;why-you-collectively-need-primitives&quot;&gt;Why you collectively need primitives&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://tokenpedia.ds.house/terms/primitive/&quot;&gt;The definition of a primitive&lt;/a&gt; is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A tier of token meant to have a &lt;strong&gt;permanent value assigned&lt;/strong&gt;…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An example of this might be &lt;code&gt;color-red-500&lt;/code&gt; which a person should reasonably expect the color red to be represented as a value, perhaps in the middle of a set of reds. A clear benefit to primitive tokens is their ease of reference. In other words, it is much easier to say and remember “color red five hundred” than it is to reference the related color channels that represent this specific color of red. This is helpful to communicate the idea across people and teams. &lt;em&gt;People need primitives.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-life-of-a-token&quot;&gt;The life of a token&lt;/h3&gt;
&lt;p&gt;Here’s a question that could split the room, when thinking about switching between light and dark expressions, &lt;strong&gt;should the value of &lt;code&gt;color-red-200&lt;/code&gt; change between these expressions?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In other words, do you expect something loosely like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-theme&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;light&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --color-red-200&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; color-mix&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(in oklch&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; red&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; white 20&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-theme&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;dark&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --color-red-200&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; color-mix&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(in oklch&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; red&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; black 20&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, the light theme has the &lt;code&gt;200&lt;/code&gt; value represent a lighter red. While the dark theme has the same &lt;code&gt;200&lt;/code&gt; value represent a darker red.&lt;/p&gt;
&lt;p&gt;If you expect the value of &lt;code&gt;color-red-200&lt;/code&gt; to change in this way, this is fundamentally &lt;em&gt;not&lt;/em&gt; a primitive by definition. Primitive tokens are meant to represent permanent values. They are meant to act like constants in the system, just like how you might store an API key and then use it later. The key’s value is too hard to recall, so storing it in something human readable is helpful.&lt;/p&gt;
&lt;p&gt;Why we expect them to be permanent is to meet user expectations. Otherwise, the mental model of token &lt;code&gt;color-red-200&lt;/code&gt; needs to switch between the expression of light and dark. In one context &lt;code&gt;200&lt;/code&gt; would need to mean lighter, and in another &lt;code&gt;200&lt;/code&gt; would need to mean darker. It’s easy to get these confused when trying to select which one from the set you are supposed to use. Is &lt;code&gt;color-red-700&lt;/code&gt; the light one now, or is it &lt;code&gt;color-red-200&lt;/code&gt;? This is the job for semantic tokens which don’t need to know if the value is light or dark. Semantic tokens don’t describe values, they describe &lt;em&gt;intention&lt;/em&gt; and how we express those intentions can change. We can choose a different red from the set of reds. But we shouldn’t change what &lt;code&gt;color-red-200&lt;/code&gt; means. The agreement of how the primitives are prepared should be stable and meet expectations for the organization.&lt;/p&gt;
&lt;p&gt;This is especially the case when a brand changes from blue to red, as was the case for AirBnB. Assigning &lt;code&gt;color-blue-500&lt;/code&gt; to the logo might have seemed reasonable at the time. But once the new expression was introduced, it certainly doesn’t make sense to now change the assignment to a red shade for &lt;code&gt;color-blue-500&lt;/code&gt;. This would break expectations for the people using the system. A semantic token meant to describe where the brand color is shown would be more appropriate, and we can later reassign the value of that from blue to red or any other special expression in the future.&lt;/p&gt;
&lt;h2 id=&quot;why-you-yourself-dont-need-primitives&quot;&gt;Why you yourself don’t need primitives&lt;/h2&gt;
&lt;p&gt;At the point I hope I’ve illustrated the reason why tokens like &lt;code&gt;color-red-500&lt;/code&gt; are meant to be permanent and why they can be helpful to us. But the system itself doesn’t need them, especially a &lt;a href=&quot;/posts/truly-semantic/&quot;&gt;truly semantic&lt;/a&gt; one.&lt;/p&gt;
&lt;p&gt;Let’s compare the following two blocks of CSS:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --error-text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; #9e2000&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --error-text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--color-red-700)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Other than one having a more human readable value, &lt;strong&gt;what is the difference between these approaches?&lt;/strong&gt; Because the primitive is meant to be permanent, and the hard coded value &lt;em&gt;is&lt;/em&gt; permanent, there isn’t much difference. You could argue that if we’re going to reuse the &lt;code&gt;--color-red-700&lt;/code&gt; in multiple places, then having a variable is helpful. But think about the &lt;em&gt;act&lt;/em&gt; of reusing the variable. How would you reuse it? In a very basic sense, you would copy and paste the primitive in the places that it is meant to be reused. Well, you’d also do the same if you use the hexcode directly. You’d copy and paste it in the places it was meant to be reused.&lt;/p&gt;
&lt;p&gt;“But that’s not how I assign these values,” you say. “I don’t select from a list of hexcodes, I select from a list of names.” Precisely! You use your meaty eyes to look for the name in a dropdown somewhere, but there’s nothing keeping that dropdown from being the set of values themselves other than those meaty eyes of yours. This could be a dropdown of binary numbers for color. The result would be the same, but because you can’t read the value, you are uneasy about using the value. It just feels better when it’s readable.&lt;/p&gt;
&lt;p&gt;In fact, in a truly semantic system, the amount of times you’d be choosing a primitive should also be very small, maybe even once! This starts going into how to think of semantic naming and further using contextual expressions; a tangent to the current topic. But a good way to tell if some system could be more semantic is the number of times you use a single primitive. If you find yourself using it several times in a system, it’s possible that these assignments could be reduced to a more generic semantic token for them all. As always, my goal is having a small set of overall tokens, and removing the primitive tier completely is certainly less maintenance overall.&lt;/p&gt;
&lt;p&gt;You might think this is only viable when you are maintaining a single theme. However, I’ll argue this really hits home when you are supporting multiple themes. When supporting multiple themes, you could have v1, v2, and v3 of these themes. You could have themes for different seasons, different promotions, and different reasons. So instead of revisiting the same file over and over again, you make a new theme as a new file. A new collection of assignments to semantic tokens. The semantic tokens stay the same, but the assignments change.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;That’s what really changes.&lt;/em&gt; It’s not the tokens that change, it’s how you assign them. Semantic tokens are a proxy to the experience, but primitives should never be found there. Primitives should only exist in the exercise of assigning values, if at all! It is there in the act of curating that you might as well just use the raw code since you might never return to this theme. You might just create a whole new one from scratch for the next product need, with wholly new values to create that new expression.&lt;/p&gt;
&lt;h2 id=&quot;for-humans-and-human-like-systems&quot;&gt;For humans and human-like systems&lt;/h2&gt;
&lt;p&gt;The reality is that you most likely aren’t going to get rid of your primitive tier, especially in large organizations with lots of ideas being shared. People need to discuss design decisions and agreements between other humans. As system professionals, organization seems like the right thing to do and having these sets keeps our decisions organized. And as AI continues to copilot our work as if it was another human in the room, we need to meet its expectations too. We also need to verify that it’s doing the right thing with our meaty eyes.&lt;/p&gt;
&lt;p&gt;But if it’s just a system for you, as many design systems nerds tend to tinker with, maybe consider reducing your tokens and you might wind up over the hill.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/bell-curve.jpg&quot; alt=&quot;Bell curve of hard coding the values versus organizing your primitives&quot;/&gt;&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>tokens</category><category>naming</category><category>organization</category></item><item><title>Chip Away</title><link>https://blog.damato.design/posts/chip-away</link><guid isPermaLink="true">https://blog.damato.design/posts/chip-away</guid><description>Chip, badge, pill, tag, lozenge, how about none?</description><pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the most confusing but ubiquitous patterns in design is “small text with a background color that may or may not be interactive”. To some that sounds like a button with an identity crisis but to others they might recognize this as a few names: chip, badge, pill, tag, or even &lt;a href=&quot;https://atlassian.design/components/lozenge/&quot;&gt;lozenge&lt;/a&gt;. It’s bad enough that the definition of these things is contested, but what is worse is because they are all generally so visually similar, it makes it hard for the &lt;em&gt;users&lt;/em&gt; to know what they are meant to do.&lt;/p&gt;
&lt;h2 id=&quot;lets-play-a-game&quot;&gt;Let’s play a game&lt;/h2&gt;
&lt;p&gt;As a little exercise, all of these are from the same design system. Try to guess what the name of each of them is:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/what-component.png&quot; alt=&quot;Different named components that look the same&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Before I reveal the answers, just think about my earlier definition of these things. They all match the description. Remember that users aren’t discerning the small idiosyncrasies between them, they are most likely lumping all of these into the same category “small text with background color”.&lt;/p&gt;
&lt;p&gt;Ok, next exercise. Here’s the definition of each of these components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A visual indicator used to communicate status.&lt;/li&gt;
&lt;li&gt;A compact label used to classify, organize, and categorize information.&lt;/li&gt;
&lt;li&gt;Used to display numeric status data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The definitions here might give you a hint about which of these components they are assigned but &lt;em&gt;only&lt;/em&gt; because of the content found in the examples. Again, think about what is more likely happening. A designer is probably thinking I need “small text with background color” and every one of these satisfies that criteria. How often do we find designers justifying which one of these things they are meant to use? I’d argue it’s more common to have a preconceived notion of the visual design without thinking &lt;em&gt;why&lt;/em&gt; they are choosing that approach. It’s usually gets reduced to “I’ve seen it before” or “this other big company does it” without much of a conversation to justify the decision. We’re encoding a few whys as definitions to justify different components, but in the end we often miss how that why is helping our designers or our users.&lt;/p&gt;
&lt;p&gt;The answers are below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://atlassian.design/components/badge&quot;&gt;Badge&lt;/a&gt; is a visual indicator for numeric values such as tallies and scores.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://atlassian.design/components/lozenge&quot;&gt;Lozenge&lt;/a&gt; is a visual indicator used to highlight an item’s status for quick recognition.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://atlassian.design/components/tag&quot;&gt;Tag&lt;/a&gt; is a compact label used to classify, organize, and categorize information.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;filters-and-categorization&quot;&gt;Filters and categorization&lt;/h2&gt;
&lt;p&gt;The following examples are from &lt;a href=&quot;https://uxcel.com/&quot;&gt;Uxcel&lt;/a&gt;, specifically &lt;a href=&quot;https://app.uxcel.com/lessons/anatomy-694&quot;&gt;their page on “UI Chips”&lt;/a&gt;. Take a close look and try to figure out the difference:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/what-difference.png&quot; alt=&quot;A do and don&apos;t image that look very similar&quot;/&gt;&lt;/p&gt;
&lt;p&gt;These are the first images of their explainer where one of these is described as a “do” and the other as a “don’t”. At a glance, it’s very hard to tell which the do and don’t is. Here’s the text from their explainer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You might wonder how chips differ from buttons. They can look similar, but they work differently. Chips, especially when used as filters, help users narrow down or adjust what they see without leaving the current view…&lt;/p&gt;
&lt;p&gt;Buttons, on the other hand, are for clear, single-purpose actions like “Save,” “Submit,” or “Cancel.” They’re used when the user needs to make a decision or move to the next step.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But isn’t filtering an &lt;em&gt;action&lt;/em&gt;? To filter is a verb, therefore an action. And each of these chips is filtering meant for a single-purpose, to filter the entities represented by the label. Buttons aren’t meant to leave the current view either, they are meant to execute an action.&lt;/p&gt;
&lt;p&gt;It’s very easy to pick the definition apart to show that there’s not much difference happening here, not just visually but also behavior. The only difference that I could glean from these is that the labels on the chips are nouns, representing the entities that will be affected once you tap the button. Meanwhile, the buttons have verbs telling you what will happen. For buttons, this is good practice. The label is describing an action, and a user reading that label can assume interacting with this element will perform the action on the label.&lt;/p&gt;
&lt;p&gt;But the chips example is very ambiguous. Again, users don’t care how you classify this thing as a button or a chip. They are trying to figure out how it is going to help them. So, they’ll look at the chips example and potentially wonder what the “Vegetarian Button” does. Some folks will tap it, some won’t because it’s not clear. Now, let’s see an alternative:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/checkbox-filters.png&quot; alt=&quot;Checkbox filter pattern&quot;/&gt;&lt;/p&gt;
&lt;p&gt;I know what you’re going say: “Donnie, that’s too much text!” In comparison to the original, absolutely. However, you can’t argue that this approach is more clear about how I’m meant to interact with this UI, and what the result will be. If the amount of text on each label is truly bothering, consider a hybrid approach:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/checkbox-legend.png&quot; alt=&quot;Checkbox filter with legend pattern&quot;/&gt;&lt;/p&gt;
&lt;p&gt;But hold on, are those chips in the original comparison image &lt;em&gt;actually&lt;/em&gt; filters? Take another look, because these things &lt;em&gt;could&lt;/em&gt; simply be describing the attributes of the location, and not actually do anything if you tap them. If that’s the case, then the image barely matches their description of a chip! If these chips aren’t filters, then it’s just a stylized list.&lt;/p&gt;
&lt;h2 id=&quot;schrödingers-button&quot;&gt;Schrödinger’s button&lt;/h2&gt;
&lt;p&gt;This is ultimately the largest problem with this pattern. It is very often a guessing game to figure out if this thing is interactive. Take a look at this example and ask yourself, can I click the element that says “Pro”?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/can-i-click.png&quot; alt=&quot;User info lockup&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The answer is 🤷, it’s Schrödinger’s button. You only know if you can interact with it until you do. Until then you are left guessing about if you can. You might think, what is the harm of tapping the button to find out? You might be surprised in &lt;a href=&quot;https://link.springer.com/chapter/10.1007/978-3-031-59080-1_10&quot;&gt;research&lt;/a&gt; that shows a type of &lt;a href=&quot;https://en.wikipedia.org/wiki/Koumpounophobia&quot;&gt;koumpounophobia&lt;/a&gt;; a fear of buttons:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;…older adults had many negative emotional user experiences, including fear of pressing buttons and embarrassment of incompetence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, it is possible that your user won’t even try and never discover something that you felt was useful because they don’t know what “Pro” does. Does it work like a checkbox and revoke the “Pro” status for this person? Does it tell me what a “Pro” is? Does it tell me &lt;em&gt;why&lt;/em&gt; this person is a “Pro”? The fact is, this element is probably not interactive. So, don’t make it look interactive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/pro-profile.png&quot; alt=&quot;Pro is within avatar&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Or if it is interactive, tell me what it will do. Make it a verb. Be deliberate in this decision. And of course, make it look like a proper button.&lt;/p&gt;
&lt;p&gt;You should also ask, does this need to be in the interface at all? Is it important that a person know this is a pro user? Ask yourself what is that importance. It’s true that &lt;a href=&quot;https://www.sciencedirect.com/science/article/abs/pii/S1567422319300110&quot;&gt;social comparison is a motivating factor&lt;/a&gt; and seeing someone with a physical conference badge walking around your city can have a fleeting thought “I wonder what they are showing at that conference?” So, having a digital badge on your profile, like a verified badge has a similar effect. If that’s the case, consider using other treatments that show status that are clearly not interactive, such as typographic hierarchy to show importance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/pro-member-hierarchy.png&quot; alt=&quot;Pro member using typographic hierarchy&quot;/&gt;&lt;/p&gt;
&lt;p&gt;In this example Cally is both a “Pro Member” and a “Photographer”. We display a slight difference between the status that was given to user from the status that was chosen by the user. If icons are well understood within the product’s ecosystem, you can consider adding an icon to further highlight the status.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;Icons also have the possibility of suggesting interactivity. If you’re going to use an icon to direct the user’s attention to this status, it should be a well-established pattern first. The icon used here should already be prominently used to indicate the status in other patterns. Otherwise, it might have the same effect as “small text with a background color that may or may not be interactive” even without the background.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;tag-inputs&quot;&gt;Tag inputs&lt;/h2&gt;
&lt;p&gt;Sometimes, this element is used in combination with a text field. The tag input is a pattern where you have an input field that allows you to enter text, and when you hit a special key (like a comma), it creates an entity with the text you entered. The benefit of this pattern is that a user can create several entities on the fly. Otherwise, the user would need to create an entity, give it a label, and save it for each piece of information they want to enter. With a tag input, they can just keep entering text and creating new entities without having to go through the process of creating and saving each one individually.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/tag-input.png&quot; alt=&quot;YouTube tag input&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The challenge with this pattern is accessibility. When keyboard navigating to each chip, how does it focus? At first you’d expect to focus the entire chip, but then the next tabbable element would be the “x” button inside the chip. This means that technically we have an interactive element inside of another interactive element, which is an accessibility violation. This also requires someone to tab twice to move to the next tag.&lt;/p&gt;
&lt;p&gt;The more appropriate approach is to make the tag a non-interactive element, and only make the “x” button interactive. This way, when you tab to the next tag, it will focus the “x” button, and you can press it to delete the tag if you want. This also means that you can easily move to the next tag without having to tab twice. Think about it, why would you want a person to focus a newly created tag if the only thing they can do with it is delete it? It doesn’t make sense to have a person focus something that they can’t interact with, and then require them to tab again to interact with it. It’s much more efficient to just focus the interactive element directly.&lt;/p&gt;
&lt;p&gt;Another note about accessibility for this pattern is that when you create a new tag, you should announce it to screen readers. This can be done using an ARIA live region, which will allow screen readers to announce the new tag as soon as it’s created. This way, users who rely on screen readers will be aware of the new tag and can interact with it if they choose to.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; aria-live&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;polite&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; aria-atomic&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;sr-only&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;announcer&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then when a new tag is created, you can update the content of the live region:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;announcer&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.textContent &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; `Tag added: &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;. Press Backspace to remove.`&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it is common to visually put instructions on how to use the tag input below the input field, it would be better to show this information &lt;em&gt;above&lt;/em&gt; the field. This goes for any input field, but especially for fields that have uncommon behavior, such as eating commas to create tags. Imagine how confusing it would be to have an empty canvas and not know what you’re supposed to be doing with it until later. Having instructions upfront prepare the user for what is about to come.&lt;/p&gt;
&lt;p&gt;Don’t forget that users can also dictate in accessible technologies, and depending on the user’s settings, commas may be inserted automatically instead of deliberately. It is possible that for some users, speaking the word comma will enter the literal word “comma” instead of the punctuation. This is something to consider when designing the interaction for this pattern, as it may not be intuitive for all users. There’s also no reliable way to know if the user is dictating, as this is often supplied by the operating system. This also doesn’t account for an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor&quot;&gt;Input Method Editor (IME)&lt;/a&gt; for non-Latin languages, which may have their own unique behaviors when it comes to punctuation and special characters. It’s important to consider these edge cases when designing the interaction for a tag input, and to provide clear instructions for all users on how to use the feature effectively.&lt;/p&gt;
&lt;p&gt;Unless, you simplify by using a pattern that nearly every developer has built as their first project:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/todo-app.png&quot; alt=&quot;Todo App&quot;/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No delimiter parsing&lt;/li&gt;
&lt;li&gt;No dependence on comma&lt;/li&gt;
&lt;li&gt;No IME conflicts&lt;/li&gt;
&lt;li&gt;No dictation ambiguity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a much more straightforward approach, and it still allows users to create multiple tags on the fly without having to go through the process of creating and saving each one individually. It’s also more accessible for users who may have difficulty with the original pattern, as it doesn’t rely on specific key presses or punctuation to create new tags. Overall, it’s a simpler and more inclusive design choice for a tag input feature.&lt;/p&gt;
&lt;h2 id=&quot;a-hard-pill&quot;&gt;A hard pill&lt;/h2&gt;
&lt;p&gt;While this post seems to be stripping all of the fun out of user interface design, the intention is to encourage designers to be more deliberate in their choices. To stop and think about how people outside of their tech circle might use this product. If you are going to use a pattern that has ambiguity, make sure you are doing it for a good reason. If you are using “small text with a background color that may or may not be interactive” just because it looks nice, then you might be alienating users; &lt;em&gt;users who want to pay for your product&lt;/em&gt;. It’s important to remember that design is not just about aesthetics, but also about usability and accessibility. By being more intentional in our design choices, we can create interfaces that are not only visually appealing but also easy to use and understand for all users.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>typography</category><category>components</category><category>design</category><category>naming</category></item><item><title>Fit Text Responsibly</title><link>https://blog.damato.design/posts/fit-text-responsibly</link><guid isPermaLink="true">https://blog.damato.design/posts/fit-text-responsibly</guid><description>Be careful of the shiny new things.</description><pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Adam Argyle made &lt;a href=&quot;https://bsky.app/profile/nerdy.dev/post/3m77ve6qvcc2q&quot;&gt;a post on Bluesky today&lt;/a&gt; announcing a new property called &lt;code&gt;text-grow&lt;/code&gt; is now available in Chrome Canary 145+. Much of the folks I follow there have been very excited about it. But I’m here to tell you to be careful.&lt;/p&gt;
&lt;h2 id=&quot;about-fit-text&quot;&gt;About fit text&lt;/h2&gt;
&lt;p&gt;The concept of fit text is not new. The idea is that the size of the text will adapt to each line such that every line fills the width of the container. It’s sort of like text justification, but instead of adjusting the spacing between words and letters, it adjusts the size of the text itself. Here’s the video from Adam’s post:&lt;/p&gt;
&lt;video src=&quot;https://nerdy.dev/media/text-grow.mp4&quot; controls&gt;&lt;/video&gt;
&lt;p&gt;Lea Verou &lt;a href=&quot;https://bsky.app/profile/lea.verou.me/post/3m5hyr7ldj22j&quot;&gt;asked around the internet&lt;/a&gt; to get use-cases for the behavior and while there’s no shortage of reasons why someone might want it, this calls for the Jurassic Park meme.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/your-scientists.jpg&quot; alt=&quot;Your scientists were so preoccupied with whether or not they could that they didn’t stop to think if they should&quot;/&gt;&lt;/p&gt;
&lt;h2 id=&quot;accessibility-awareness&quot;&gt;Accessibility awareness&lt;/h2&gt;
&lt;p&gt;Any time that we mess with the size of text, it makes me think of how my parents have made changes in their devices to set the text size for it to be more legible. I think about how the text we enter into our experiences is not necessarily the text that another user will see after translating it. It drives the point home that the less we try to control how text appears, the better the experiences are for the widest audience.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://nerdy.dev/css-text-grow&quot;&gt;Adam’s post on his website&lt;/a&gt;, he’s linked to several resources. One of them is the discussion about adding this feature to CSS. I want to call out some of the quotes from the discussion specifically regarding accessibility:&lt;/p&gt;
&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://github.com/w3c/csswg-drafts/issues/2528#issuecomment-2770953200&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p&gt;I just finished a research project on personalization and accessibility (under review) and the most common action taken by people who are considered low vision is to customize text size. Inability to change text size is a pretty significant barrier for many people. &lt;a href=&quot;https://www.sciencedirect.com/science/article/pii/S2214109X20304253&quot;&gt;Meta-analysis research from 2020&lt;/a&gt; estimates that around 600 million people worldwide would be considered on the spectrum of “low vision” to blind, which was roughly 8% of the world population in 2020. These numbers are expected to grow, due to compounding socio-economic factors, by 2050. This isn’t just a small slice of people.&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/2528#issuecomment-2770953200&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt;frankelavsky&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://github.com/w3c/csswg-drafts/issues/2528#issuecomment-2772072339&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p&gt;Making text dependent on the viewport has always been problematic for accessibility, this is well documented. This is why we usually advise not to use clamp or vh/vw units in font sizes. It just overrides user preferences unnecessarily.&lt;/p&gt;&lt;p&gt;The only way this could pass muster is to require browsers to include an opt-out to the behavior. Or to ignore it when users zoom in, which would defeat the purpose of the proposal.&lt;/p&gt;&lt;p&gt;I second Patrick’s pointer at LVTF and general W3C horizontal review. It might be good to generally outline when you get specific accessibility input to ensure that proposals are feasible to implement so that all users can use them.&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/2528#issuecomment-2772072339&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt;yatil&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/explainers-by-googlers/css-fit-text/blob/main/README.md&quot;&gt;explainer&lt;/a&gt; mentions that there’s still an open question about accessibility for this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If an end-user tries to enlarge font size, UAs should not fit enlarged lines to the container width. Is minimum-font setting enough?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My worry is similar to the one I have with folks reordering elements visually with CSS. Just because we have CSS technologies, doesn’t necessarily mean that they should be used in any situation. It’s not often that folks really consider the impact of every CSS decision. The large majority of developers are just picking the first solution that works and move on. Taking a moment to consider how our decisison affect more than us is a fading responsibly.&lt;/p&gt;
&lt;h2 id=&quot;maybe-not-for-screens&quot;&gt;Maybe not for screens&lt;/h2&gt;
&lt;p&gt;All that said, I think this can be used responsibly. I think for folks using CSS for print media, this could be a useful tool since after text is printed there’s no way of having user settings affect it. Many of the folks that provided visual examples in Lea’s post providing media meant for printing. In reading some of the other replies, it really looks like many of the use-cases are for non-web things. &lt;a href=&quot;https://x.com/KrisDesChacals/status/1988889941707796768/photo/1&quot;&gt;This example&lt;/a&gt; is from Kris des Chacals asking for this property for use in &lt;a href=&quot;https://obsproject.com/&quot;&gt;OBS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/G5nk67fWkAAxen5?format=png&amp;name=small&quot; alt=&quot;Displaying a question and answers like &amp;#34;Who Wants to be a Millionaire&amp;#34;&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Maybe the problem is that CSS is becoming too powerful and now we want to use it for everything. I know I do. 😅&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>typography</category><category>css</category></item><item><title>Minefield Context Protocol</title><link>https://blog.damato.design/posts/minefield-context-protocol</link><guid isPermaLink="true">https://blog.damato.design/posts/minefield-context-protocol</guid><description>Being ahead of the curve can make you a pawn on the field.</description><pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;AI is everywhere in tech now. It is in the tools we use, the products we build, and the way we interact with users. It feels like if you aren’t with AI, you are behind. While there’s plenty of questionable AI use, one thing is for sure: AI is making waves. And it’s really hard to keep up with the latest.&lt;/p&gt;
&lt;p&gt;One of the concepts that are gaining lots of discussion is the “MCP” which stands for Model Context Protocol. Anytime I’d ask what an MCP is, I’d usually hear it described as an API. So then, why isn’t it just called an API? Because it’s not really an API. Sound confusing? You bet!&lt;/p&gt;
&lt;p&gt;Just like how I learned to code from actually building something. I decided if I was going to truly learn what this thing was, I’d have to build one. So I did and this is how that went.&lt;/p&gt;
&lt;p&gt;If this sounds familiar to you, &lt;a href=&quot;https://www.youtube.com/watch?v=b9Zk5yDaino&quot;&gt;I did speak about this on Wireframe on Monday&lt;/a&gt;. You can think of this as the accompanying blog post. If you prefer listening to what I went through to make this, go ahead and click the link.&lt;/p&gt;
&lt;h2 id=&quot;you-have-too-much-stuff&quot;&gt;You have too much stuff&lt;/h2&gt;
&lt;p&gt;Some of the feedback that I get from folks who follow my work is that I do too many things. It’s hard to keep track of my latest research findings or development projects. Even my wife who works in the same field and lives under the same roof has trouble keeping up with my work. On top of this, much of my work is very abstract and hypothetical. It makes it hard to fully apply my work into real world scenarios without understanding all of the research behind it. Not to mention all the other factors that could contribute to friction in the implementation.&lt;/p&gt;
&lt;p&gt;So what if there was one place to learn about my work conveyed in a approachable way? Some way that you could learn about my work within the context of your own work? That’s what I set out to build, my own MCP server that aggregates my work and research findings into a single place.&lt;/p&gt;
&lt;p&gt;To do this, I needed two major units of work. I needed an MCP server to connect with MCP clients and I needed the server to fetch data from my domains in a way helpful for these systems to comphrehend.&lt;/p&gt;
&lt;h2 id=&quot;end-domain-data&quot;&gt;End-domain data&lt;/h2&gt;
&lt;p&gt;First, I wanted each of my end-domains: my personal site, the blog, and my design system to expose their data in a structured way. It was important to me that I’d follow some acceptabled standards for exposing this data. My thought was that I wanted these sites to opt-in for AI discovery.&lt;/p&gt;
&lt;p&gt;The way I found to do this is with using a &lt;code&gt;llms.txt&lt;/code&gt; file which has &lt;a href=&quot;https://llmstxt.org/&quot;&gt;an emerging standard&lt;/a&gt;. This file is like a sitemap but for AI. The content is actually markdown. Here’s a sample:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;md&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground);font-weight:bold&quot;&gt;# Title&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt; Optional description goes here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;Optional details go here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground);font-weight:bold&quot;&gt;## Section name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-link);text-decoration:underline&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword);text-decoration:underline&quot;&gt;Link title&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-link);text-decoration:underline&quot;&gt;](https://link_url)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: Optional link details&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground);font-weight:bold&quot;&gt;## Optional&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;- &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-link);text-decoration:underline&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword);text-decoration:underline&quot;&gt;Link title&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-link);text-decoration:underline&quot;&gt;](https://link_url)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Importantly, the links found in the file that go to content should also be markdown files. That means my site needed to serve not just the HTML pages, but also the Markdown source files. This way, the MCP server could fetch the raw content and parse it for AI consumption.&lt;/p&gt;
&lt;p&gt;For my personal site and the blog, this was fairly straightfoward because they are both written in Markdown and rendering that as raw text is a few lines of code for a &lt;code&gt;[slug].md.js&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { getCollection } &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;astro:content&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; getStaticPaths&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; entries&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; getCollection&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;posts&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; entries&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.map&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;((entry) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;            params&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { slug&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; entry&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.id }&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;            props&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { entry }&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; GET&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;({ props }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; Response&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.body&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        status&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        headers&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;text/plain; charset=utf-8&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The real problem was Storybook. Since the source for the files there are written in &lt;code&gt;stories.tsx&lt;/code&gt; files, it’s not a simple 1:1 translation. Admittedly, I vibe-coded the solution to this and wound up with nearly 1000 lines of code. Luckily, my system is open-source so you can &lt;a href=&quot;https://github.com/damato-design/system/blob/main/scripts/generate-llms.js&quot;&gt;grab this&lt;/a&gt; and remix it how you like for your own purposes.&lt;/p&gt;
&lt;p&gt;The script does a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Finds all the &lt;code&gt;stories.tsx&lt;/code&gt; files in the &lt;code&gt;src/components&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Parses the file to extract  JSDoc, component category and name, and stories.&lt;/li&gt;
&lt;li&gt;Determines the props from the imported component type definition.&lt;/li&gt;
&lt;li&gt;Renders component stories as JSX instead of the less useful Story source.&lt;/li&gt;
&lt;li&gt;Generates a Markdown file for each component and a &lt;code&gt;llms.txt&lt;/code&gt; file with all of the components.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this in place, all of my end-domains are now exposing their data in a structured way for AI discovery and consumption.&lt;/p&gt;
&lt;h2 id=&quot;the-mcp-server&quot;&gt;The MCP server&lt;/h2&gt;
&lt;p&gt;My MCP server is a very simple Netlify project. It was heavily inspired by &lt;a href=&quot;https://developers.netlify.com/guides/write-mcps-on-netlify/&quot;&gt;this guide on Netlify&lt;/a&gt;. Much of the scaffolding is the same except registering tools and resources which is the main difference.&lt;/p&gt;
&lt;h3 id=&quot;resources--tools&quot;&gt;Resources &amp;amp; Tools&lt;/h3&gt;
&lt;p&gt;When I went into this project, I was under the impression that everything I was going to offer from this server would have been resources. Resources are basically static files that the server can provide. It’s like requesting a library book. You receive the book with no modifications to the content, just as it is.&lt;/p&gt;
&lt;p&gt;For resources there’s two actions that clients need to accept: list resources and get resource. List resources is telling the client what is available, and get resource is fetching the content of a resource. This maps 1:1 to what was created at the end-domains earlier. The &lt;code&gt;llms.txt&lt;/code&gt; is the list of resources, while each of the Markdown files is a get. The slightly annoying thing is that we need to transform the &lt;code&gt;llms.txt&lt;/code&gt; Markdown into structured data. While I made my own using the Markdown AST tools, there seems to be &lt;a href=&quot;https://www.npmjs.com/package/parse-llms-txt&quot;&gt;a newly created package&lt;/a&gt; that might help in the parsing. Though I take pause to it not having a link to the source to know what it is doing.&lt;/p&gt;
&lt;p&gt;Once you have it parsed, you can register each as a resource. For my MCP, I wanted the resources to have the following URI structure:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;subdomain://category/slug&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The MCP specification allow you to make up your own URI scheme and my sites generally follow this shallow pattern. This is a resource URI meant for the server to use internally to look up its resources. Then you can use &lt;a href=&quot;https://github.com/modelcontextprotocol/typescript-sdk&quot;&gt;the official MCP Typescript SDK&lt;/a&gt; to register the resource template assuming you have &lt;code&gt;LIST_RESOURCES_META&lt;/code&gt; to configure the resource and a function &lt;code&gt;listResources()&lt;/code&gt; to handle the fetching:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.registerResource&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;    &amp;#39;resources&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    new&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; ResourceTemplate&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;{subdomain}://{category}/{slug}&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { list&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; undefined&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; })&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    LIST_RESOURCES_META&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    listResources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The thing that bothered me is that &lt;code&gt;{ list: undefined }&lt;/code&gt; setting. It was unclear what the purpose of that was, and after some vibe-coding sessions, Claude suggested I use the &lt;code&gt;server.setRequestHandler()&lt;/code&gt; method to define resources. So, if you’re building something similar, you might want that method over the helper methods found prominently in the &lt;code&gt;README.md&lt;/code&gt; for the SDK. Especially since my resources are dynamic; they don’t truly exist on the server.&lt;/p&gt;
&lt;p&gt;A gotcha here is that some MCP clients, systems that speak to MCP servers, don’t support reading resources. They need a tool to access the resources. So, that required that I include tools. Tools is fancy word for something that can process data. Generally we’d think of something like a POST request for an API. In this case, in order for some clients to access the resources, I needed to create a pathway for them to make the request. For my system, it was recommended to use the &lt;code&gt;server.setRequestHandler()&lt;/code&gt; again, but as per the &lt;code&gt;README.md&lt;/code&gt;, registering a tool is done like this, assuming you have a &lt;code&gt;GET_RESOURCE_META&lt;/code&gt; to configure the tool and a function &lt;code&gt;getResource()&lt;/code&gt; to perform the lookup:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.registerTool&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;    &amp;#39;get_resource&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    GET_RESOURCE_META&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    getResource&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;stdio-vs-https&quot;&gt;STDIO vs HTTPS&lt;/h2&gt;
&lt;p&gt;Another thing that was being thrown around as common knowledge was MCP servers and clients having better support for STDIO over HTTPS. Because I program primarily on the web, I didn’t know what STDIO was. I just thought it was some other way of making the same server. In reality, STDIO is a way of identifying a MCP server than is meant to be installed locally on a persons computer. The HTTPS MCP server is a more recent development, which explains why I found it really hard to develop and test. My way of testing was just pushing the server up and hitting it in Cursor to see if I could connect and have it respond. Definitely not scalable, and I hope there’s better tools out there that can help in this area. The Netlify article speaks about making a hybrid server, and there’s something called &lt;a href=&quot;https://github.com/modelcontextprotocol/mcpb&quot;&gt;MCP Bundles&lt;/a&gt; that could be cool to try. Though I prefer the HTTP version. I find hitting a URL much easier than downloading and installing something.&lt;/p&gt;
&lt;h2 id=&quot;what-now&quot;&gt;What now?&lt;/h2&gt;
&lt;p&gt;The server is located at &lt;a href=&quot;https://mcp.damato.design&quot;&gt;&lt;code&gt;mcp.damato.design&lt;/code&gt;&lt;/a&gt; and the homepage has instructions on how you can add it to your MCP client of choice. This helps show my aptitude with AI technologies, while providing access to my research in a contextual way. It was filled with areas of my own learning and seeing all of the gotchas and missing standards shows how much people are still trying to figure all of this out. It was effectively a weekend project, and something you might want to consider trying to keep your skills in the game. Happy vibing?&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>artificial intelligence</category><category>organization</category></item><item><title>Complementary Space</title><link>https://blog.damato.design/posts/complementary-space</link><guid isPermaLink="true">https://blog.damato.design/posts/complementary-space</guid><description>Design spacing tokens semantically.</description><pubDate>Sun, 16 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Before &lt;a href=&quot;https://mode.place&quot;&gt;Mise en Mode&lt;/a&gt;, I went on a journey to try to truly understand what it meant to have a design token be “semantic”. The community had a fairly good understanding about what this meant as color, and the benefits that comes from thinking in this way. Text has been perhaps more elusive. Though in recent years we are seeing more and more teams separating the meaning of an “h2” from its visual presentation. Separating the reason why something exists from its style is what semantics is all about.&lt;/p&gt;
&lt;p&gt;What was first &lt;a href=&quot;/posts/spacing-solved&quot;&gt;a blog post here&lt;/a&gt; turned into six months of research now titled &lt;a href=&quot;https://complementary.space/&quot;&gt;Complementary Space&lt;/a&gt; which aimed to rethink space in the same way. What does it mean to apply space somewhere, how would we name those tokens, and most importantly how to convey the concepts of being comfortable or cozy through these tokens.&lt;/p&gt;
&lt;p&gt;This post is meant to revisit the article that I wrote those years ago, share some insights I’ve landed on, and finally to be a memorial as I expect to eventually take the original web page down to reduce cost for all of these domains I have. 😅&lt;/p&gt;
&lt;h2 id=&quot;gestalt-proximity&quot;&gt;Gestalt proximity&lt;/h2&gt;
&lt;p&gt;The first concept to grasp is the purpose of space which is covered by the &lt;a href=&quot;https://www.nngroup.com/articles/gestalt-proximity/&quot;&gt;Gestalt principle of proximity&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The principle of proximity states that items close together are likely to be perceived as part of the same group — sharing similar functionality or traits.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In short, when we apply space somewhere, what we’re doing is conveying a relationship between objects. When there’s more space between items, they are less related than items that are more closely related.&lt;/p&gt;
&lt;p&gt;This means that the value that we assign to spacing properties is describing a relationship. There’s two kinds of relationships that we see, parent-child and sibling relations. Parent-child relationships tend to have a larger amount of space, or in terms of relationships, are slightly more distant than siblings. This is a common visual paradigm, where the padding around the card holding the children inside is larger than the gaps between those children (ie., siblings).&lt;/p&gt;
&lt;h2 id=&quot;periscope&quot;&gt;Periscope&lt;/h2&gt;
&lt;p&gt;Historically, we’d use something like a T-shirt scale to say how much space are between the objects. However, as I’ve said before, &lt;a href=&quot;/posts/truly-semantic&quot;&gt;scales aren’t semantic&lt;/a&gt;. It isn’t clear where any of these tokens are meant to go in the experience. It’s subjective.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/non-semantic-tokens.png&quot; alt=&quot;Button color is semantic, space-4 is not&quot;/&gt;&lt;/p&gt;
&lt;p&gt;What I landed on after much development is to &lt;strong&gt;have the scope describe the density of a region, not the tokens&lt;/strong&gt;. To get a better understanding of the idea, let’s watch a moment from the TV show MacGyver. Specifically this clip from episode called “Hell Week”.&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; style=&quot;aspect-ratio: 16/9&quot; src=&quot;https://www.youtube.com/embed/oPQ-5H_AU18?si=ySZieleRVeFpASIh&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;In the clip, the male student is competing in a contest which is meant to keep other students from entering their dorm. Each student devises a way to keep the door from opening but all have some solution that allows entry. This student’s idea was to make a miniture version of the room. Everything is copied in detail so that when you look through the door’s eyehole, you see the miniture but it looks like a normal size room.&lt;/p&gt;
&lt;p&gt;The point of this example is that trying to determine the space between objects without knowing the frame of reference makes it so those distances are visually equal. Certainly, if we are inside of the room we can see, and more importantly &lt;em&gt;measure&lt;/em&gt;, the distance between the bed and desk. When we do this, we can immediately tell that the distance between these items in the life-sized room is much larger than the model. But through the eye-hole you can’t because the frame or reference is gone. Everything is proportional. This is how scale models work. You measure the life-size objects, and then apply some ratio to scale them up or down. Through different lenses these can appear the same but in the larger environment actually be different sizes.&lt;/p&gt;
&lt;p&gt;Another way of thinking about this is the distance between you and your front door in comparison to the distance between you and the nearest ocean. The nearest ocean is probably very “far” for most people in comparison to their front door. Now, think about the distance between you and the nearest ocean, and the distance between you and the moon. Suddenly the distance between you and the ocean isn’t far at all. Changing the frame of reference is changing what it means to be far.&lt;/p&gt;
&lt;p&gt;The point here is that we can change what it means to be “far” by changing the scope. And because our goal is to semantically define what it means to be far, all we need to do is have different “scopes” that express different kinds of far. More importantly, when I say “far”, I really mean space.&lt;/p&gt;
&lt;p&gt;In the following image, I’ve made a component that could either represent the entire page or a card within the page.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/page-or-card.jpeg&quot; alt=&quot;Is it a page or is it a card?&quot;/&gt;&lt;/p&gt;
&lt;p&gt;I use the same base composition for both of these things. On the right, the smaller components found within the larger one are really just scaled down versions of the larger one. Certainly, we see things like typography scaled down, but we also see the space scaled down too.&lt;/p&gt;
&lt;p&gt;In this way, we don’t see the page as a whole. Instead we zoom into individual scopes of the composition and then decide how dense that region should be. Generally the deeper we go, the more dense the composition is expected to be.&lt;/p&gt;
&lt;h2 id=&quot;what-are-the-tokens&quot;&gt;What are the tokens?&lt;/h2&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;In the original article, I recommend having two tokens “near” and “away” to signify close and far relationships. Since writing this article, I’ve found that even this decision can be confusing. While in later recommendations and even examples, I suggest having a single token. &lt;a href=&quot;https://system.damato.design&quot;&gt;In practice&lt;/a&gt;, I’ve renamed these tokens to “padding” and “gap” which represent the parent-child and sibling relationships through space more clearly.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;In all of this research, it was important to me that I could land at a similar outcome to what Nathan Curtis had written about in &lt;a href=&quot;https://medium.com/eightshapes-llc/space-in-design-systems-188bcbae0d62&quot;&gt;his work on spacing tokens&lt;/a&gt; back many years prior but to have this new way of understanding space. Here’s his approach from the article and the complementary approach side-by-side.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/cards-nathan-complementary.png&quot; alt=&quot;Clean card, Nathan&apos;s approach, and Complementary Space&quot;/&gt;&lt;/p&gt;
&lt;p&gt;In Nathan’s approach, you’ll see that each space token describes composition of the space and its amount. Inset means it goes around content, stack means it goes between elements on adjacent sides, and gap means it goes between on all sides. In this way, there’s lots of decisions that must happen at each location where space is going to exist that also depend on the composition of the elements.&lt;/p&gt;
&lt;p&gt;In Complementary Space you’ll see that there is a single token placed for every location we expect to represent some amount of space. This immediately reduces decision making because there is only one token to choose. This means that the only decision that a person needs to make is the “scope” of the content. In other words, am I expecting to reduce the context down to a lower level of hierarchy than the parent above. Where these scopes occur are represented by the dotted borders surrounding regions of the card in the example.&lt;/p&gt;
&lt;h2 id=&quot;what-else-do-i-need&quot;&gt;What else do I need?&lt;/h2&gt;
&lt;p&gt;Below is a interactive demo of the approach, showing how we can make a region more dense by adding the &lt;code&gt;data-density-shift&lt;/code&gt; attribute. See how there are several different amounts of space being applied while using very few differently named tokens, in this case &lt;code&gt;--space-near&lt;/code&gt; and &lt;code&gt;--space-away&lt;/code&gt; from the original implementation.&lt;/p&gt;
&lt;iframe height=&quot;550&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;complementary.space&quot; src=&quot;https://codepen.io/fauxserious/embed/dyJgzBM?default-tab=result&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot;&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/dyJgzBM&quot;&gt;complementary.space&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;Again, this isn’t entirely needed as demostrated from the image just before but representing &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;gap&lt;/code&gt; as describing relationships seems to describe the purpose more than a single space token.&lt;/p&gt;
&lt;p&gt;Either with one token or two, this works by compounding identical selectors. Assuming that the way something becomes more dense is by being identified with &lt;code&gt;data-density-shift&lt;/code&gt;, this is how you’d create the levels:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-density-shift&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] {...}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-density-shift&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-density-shift&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] {...}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-density-shift&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-density-shift&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-density-shift&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] {...}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we duplicate the &lt;code&gt;data-density-shift&lt;/code&gt; attribute selector which shows how deep the region is related to other density shifts. While you could have more control by explicitly giving each shift a number related to depth, this brings a subjective decision in an otherwise binary decision of “should this be more dense that the region above?” This also brings more care to the hierarchy of the page. Things that are less dense are more spread out and unrelated sections of content are clearer than if they were composed without these considerations.&lt;/p&gt;
&lt;p&gt;Within each level, we expect the spacing values to decrease, making compositions appear more dense. In the CodePen, I create a few Sass functions to help generate the scale based on the number of levels. In the code below I’ve updated to use &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;gap&lt;/code&gt; instead of the earlier &lt;code&gt;near&lt;/code&gt; and &lt;code&gt;away&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;$attr-sel&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;[data-density-shift]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;$levels&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/* including body */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/* Fibonacci Sequence */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; fib&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;($n) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;  @return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;($n &amp;lt;= &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; fib&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;($n &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; fib&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;($n &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@mixin&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; vars&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;($n) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --padding&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    #{&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;fib&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;($n &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1)} &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--density&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 8&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --gap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    #{&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;fib&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;($n)} &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--density&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 8&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@for&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; $i &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; through&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; $levels {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  $nest-sel&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;($i == 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;body&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; selector-nest&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;($nest-sel&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; $attr-sel))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  #{$nest-sel} {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    @include&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; vars&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;($levels &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; $i);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might also notice that I’ve replaced the usage of &lt;code&gt;rem&lt;/code&gt; with &lt;code&gt;px&lt;/code&gt; in the code above. &lt;a href=&quot;/posts/font-size-dimensions/&quot;&gt;This is a separate topic&lt;/a&gt; but in short, if the user increases the font size, this is meant to help with readability and the user isn’t meaning to “read” space. Therefore, using units unrelated to typography are preferred.&lt;/p&gt;
&lt;p&gt;Speaking of values, this is an area where you might want to explore even further. For example, on touch devices, you might consider having more space than on devices where a pointer is used. This is to increase the target area of interactive elements and make them slightly easier to interact with than using a finer pointing device. Importantly, this is not the difference between “mobile” and “desktop” &lt;a href=&quot;https://spectrum.adobe.com/page/platform-scale/&quot;&gt;as some might have you believe&lt;/a&gt;. The reason why we should be making space larger is for interactive targetting, not because something is considered mobile. Since this something that should affect the experience globally, this is a matter of updating the density under a media query.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@media&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (any-pointer&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; coarse) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;    :root&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        --density&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt; /* something a bit larger */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I recommend reading &lt;a href=&quot;https://css-tricks.com/interaction-media-features-and-their-potential-for-incorrect-assumptions/&quot;&gt;this post at CSS-Tricks&lt;/a&gt; about interaction media queries for further considerations.&lt;/p&gt;
&lt;h2 id=&quot;avoiding-size-variants&quot;&gt;Avoiding size variants&lt;/h2&gt;
&lt;p&gt;A large benefit of the scope-based approach is that size variants for your system are no longer meticiously defined. The size of an element is determined by the context it is placed within. In other words, if the button is meant to be smaller, place the button in a region meant to convey a higher density.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/button-scoped-density.jpeg&quot; alt=&quot;Button size determined by the scope&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This is assuming that other cosmetic properties of the button are also determined by the density, such as font-size.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://system.damato.design/?path=/docs/foundations-density--docs&quot;&gt;An example at my design system&lt;/a&gt; illustrates this in code in the following way where &lt;code&gt;denser&lt;/code&gt; causes the scope to increase in density (ie., space becomes smaller) and can be added to any element:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;box.div&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; denser&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; gap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; priority&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;box.div&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; denser&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; gap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; priority&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;box.div&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;box.div&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the size of the gap is wholly determined by the surrounding scope, &lt;code&gt;gap&lt;/code&gt; becomes a flag that merely indicates if space should exist between the children at all.&lt;/p&gt;
&lt;p&gt;While this does create the ability to have more levels of space than you should ever use, I recommend having 3 to start. The highest level designed to support marketing pages, the next level designed to support general productivity dashboards, and the last level to help with the composition of cards and other focused regions. This assumes that you are representing &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;gap&lt;/code&gt; as tokens. If not, you might require more levels to accurately describe different kinds of space from scope alone.&lt;/p&gt;
&lt;h2 id=&quot;when-will-you-be-ready&quot;&gt;When will you be ready?&lt;/h2&gt;
&lt;p&gt;The reality is that &lt;em&gt;we are still teaching teams semantic color&lt;/em&gt;, let alone semantic space. People continue to hard code values into places where they just can’t be bothered to do the right thing. Until we change the mindset of the industry, we’ll be stuck doing things the old-fashioned way, wondering why it’s so hard to make new expressions of our experiences.&lt;/p&gt;
&lt;p&gt;So, instead of trying to implement in your company’s design system. I recommend trying to implement into your own personal projects. That way, the understanding of how everything works doesn’t need to go beyond you. Give it a shot, and let me know how it goes? Let’s make space complementary. 🤝&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>space</category><category>tokens</category><category>naming</category><category>organization</category></item><item><title>Fluid Headings</title><link>https://blog.damato.design/posts/fluid-headings</link><guid isPermaLink="true">https://blog.damato.design/posts/fluid-headings</guid><description>Uniformly reducing headings for smaller screens.</description><pubDate>Fri, 10 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There’s no shortage of posts that explain how to perform responsive typography. Here’s a small sample of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2023/11/addressing-accessibility-concerns-fluid-type/&quot;&gt;Addressing Accessibility Concerns With Using Fluid Type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2022/12/fluid-typography-predict-problem-users-zoom-in/&quot;&gt;Fluid Typography: Predicting A Problem With Your User’s Zoom-In&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&quot;&gt;Modern Fluid Typography Using CSS Clamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2017/05/fluid-responsive-typography-css-poly-fluid-sizing/&quot;&gt;Fluid Responsive Typography With CSS Poly Fluid Sizing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2016/05/fluid-typography/&quot;&gt;Responsive And Fluid Typography With vh And vw Units&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that’s just articles from a single website!&lt;/p&gt;
&lt;p&gt;However, in those articles no one really mentions what qualities you are meant to look out for when figuring out the values. I’ll admit the closest article to this is the first article which explains that if you use only viewport units for a &lt;code&gt;font-size&lt;/code&gt; that it’ll break the zoom expectations in the browser, which will cause a &lt;a href=&quot;https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1#resize-text&quot;&gt;WCAG 1.4.4&lt;/a&gt; failure. The recommendation there is to &lt;em&gt;always&lt;/em&gt; include a non-viewport unit in the calculation with your viewport unit.&lt;/p&gt;
&lt;p&gt;Well, I’m about to turn that on its head. 🙃&lt;/p&gt;
&lt;p&gt;What I set out to do is to have a rule that helps explain what the transition should be for heading font sizes between large and small screen sizes. In other words, what the middle value of a CSS &lt;code&gt;clamp()&lt;/code&gt; function is expected to be.&lt;/p&gt;
&lt;h2 id=&quot;min-and-max&quot;&gt;Min and Max&lt;/h2&gt;
&lt;p&gt;First, we need a few numbers to start. The first two are up to you, your font family, and typescale. These will be the minimum size and the maximum size your heading is meant to be. For the purposes of example and to exaggerate the effect, let’s consider this in terms of marketing display text. Really big and fun text on desktop but somehow needs to be reasonable on a small device. In this example, I’m just going to make variables for these and they’ll be in the places where you’d expect in the &lt;code&gt;clamp()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text-heading&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    font-size&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-smallest)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;        /* heading change */&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-largest)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The middle value is what we’re going to calculate, and we’re going to do that with a few more numbers. A minimum viewport size and a “maximum” viewport size.&lt;/p&gt;
&lt;p&gt;The minimum size that I’ll recommend is &lt;code&gt;320px&lt;/code&gt;. This comes from the &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/reflow.html&quot;&gt;WCAG 1.4.10&lt;/a&gt; about reflow.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vertical scrolling content at a width equivalent to 320 CSS pixels&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The “maximum” is not really the maximum viewport size. It’s closer to being a breakpoint of sorts. What this size is meant to be is the point in which the largest size that the heading is meant to be starts shrinking to the smaller size. It’ll stop shrinking at &lt;code&gt;320px&lt;/code&gt;. What we’re going to explore is what that size is meant to be.&lt;/p&gt;
&lt;p&gt;My thinking for this starts with the recommendation of number of characters for body copy, which is somewhere between 40 and 80 characters depending on your source. I typically pick a number in the middle, 60 characters, to represent the optimal reading length. Importantly, this is speaking about paragraphs. This is not meant to be the same number for headings, and in my research I couldn’t find a number that represents what the optimal character length for headings is meant to be. If there is one, please let me know.&lt;/p&gt;
&lt;p&gt;To make up for this, I figured that we typically want to lockup paragraphs with the heading. We want them to visually match together. If we’re still speaking in terms of characters and the body copy can be represented as &lt;code&gt;60rch&lt;/code&gt; (assuming that the root font size dictates body copy), then we could also set the heading maximum length in terms of those characters. So that means that the “maximum” viewport width should be &lt;code&gt;60rch&lt;/code&gt; for headings.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;You might have already set a &lt;code&gt;max-width: 60rch&lt;/code&gt; on the container for this content and be wondering why we’re calling this out specifically when it comes to headings. We’ll need this number for further calculations.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;It’s also possible that this &lt;em&gt;feels&lt;/em&gt; too large for you. If you disagree and feel that the length of the heading is meant to be shorter than the related copy, then feel free to update this value. But the expectation is that this a maximum length that causes the wrapping behavior of the heading to achieve LooksGood™ status.&lt;/p&gt;
&lt;h2 id=&quot;time-for-the-math&quot;&gt;Time for the math&lt;/h2&gt;
&lt;p&gt;Ok, these will be all of the values we need to determine the middle number. Here’s the formula:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;m=(y_2-y_1)/(x_2-x_1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If that looks familiar, it’s because you may have read &lt;a href=&quot;/tags/typography/&quot;&gt;my other typographic posts&lt;/a&gt; that discuss a rate of change. This is trying to determine the slope of a line. Let’s write this in terms of CSS:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text-heading&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --m&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-largest) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-smallest))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        / (60&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;rch&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; - 320&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Except this wont work this way in CSS. We’re not able to divide with a unit number, which would be the result of &lt;code&gt;60rch - 320px&lt;/code&gt;. While you could do some fun hacks to make this work, I’m going to suggest something different: to make everything in terms of &lt;code&gt;rem&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;--heading-largest&lt;/code&gt; and &lt;code&gt;--heading-smallest&lt;/code&gt; are probably in terms of &lt;code&gt;rem&lt;/code&gt; already or something relative to &lt;code&gt;font-size&lt;/code&gt; like &lt;code&gt;rem&lt;/code&gt;. So if we update the numbers below to also be &lt;code&gt;rem&lt;/code&gt;, then we don’t need the units in the denominator. In my experience, this seems to be the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text-heading&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --m&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-largest) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-smallest))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        / (30 - 20&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/* 30rem - 20rem */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now what we need to do is relate this to how the viewport changes. I specifically recommend viewport over container because having different sized headings depending on the container instead of the context of the content could be hierarchically confusing. Having all of the same heading behave identically maintains the hierarchical relationship across all areas uniformly.&lt;/p&gt;
&lt;h2 id=&quot;scaling-text&quot;&gt;Scaling text&lt;/h2&gt;
&lt;p&gt;The last part is borrowed from &lt;a href=&quot;https://css-tricks.com/linearly-scale-font-size-with-css-clamp-based-on-the-viewport/&quot;&gt;this post on CSS-Tricks&lt;/a&gt;, which also explores responsive typography but has a special distinction: when you resize the examples, the text looks like it scales as if you wired &lt;code&gt;scale()&lt;/code&gt; to the viewport for the text. I found this to be the missing part to fluid headings.&lt;/p&gt;
&lt;p&gt;My goal was to keep the wrapping composition between the “maximum” and minimum. While Pedro’s post uses JavaScript to do the heavy lifting (because the dividing problems we mentioned earlier), we can get there in CSS. In fact, our implementation is arguably &lt;em&gt;easier&lt;/em&gt; than his because we want this change to be &lt;em&gt;linear&lt;/em&gt;. In other words, as the viewport changes, the rate of change is a straight line between the max and min. What this does is &lt;em&gt;maintain the wrapping composition of the heading&lt;/em&gt; between the max and min. This is that scaling effect from the post.&lt;/p&gt;
&lt;p&gt;Here’s how we’d finish this:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text-heading&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --m&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-largest) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-smallest))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        / (30 - 20&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/* 30rem - 20rem */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    font-size&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-smallest)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--m) * 100&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;vw&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--heading-largest)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember, the recommendation is for viewport units in the &lt;code&gt;font-size&lt;/code&gt; to be accompanied by some non-viewport units, which is what the &lt;code&gt;--m&lt;/code&gt; is providing. &lt;code&gt;--heading-largest&lt;/code&gt; and &lt;code&gt;--heading-smallest&lt;/code&gt; are font sizes you’ve set for headings.&lt;/p&gt;
&lt;p&gt;You can check a working example out on my &lt;a href=&quot;https://system.damato.design/?path=/docs/foundations-typography--docs&quot;&gt;design system playground&lt;/a&gt; or if you just want to watch the magic, I have a animated GIF below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/fluid-headings.gif&quot; alt=&quot;Resizing panel causing the font size to mantain wrapping behavior between &amp;#34;breakpoints&amp;#34;&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Hopefully, this takes less of the guess work into the numbers that are meant to appear in the &lt;code&gt;clamp()&lt;/code&gt; and maintains typographic lockups more regularly.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>typography</category><category>css</category></item><item><title>Storefront &amp; Warehouse</title><link>https://blog.damato.design/posts/storefront-warehouse</link><guid isPermaLink="true">https://blog.damato.design/posts/storefront-warehouse</guid><description>How may I help you today?</description><pubDate>Thu, 02 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was speaking with a colleague recently about design system marketing. One of the problems I’ve seen is a person coming into design system resources and being immediately overwhelmed by the options. &lt;a href=&quot;https://lawsofux.com/hicks-law/&quot;&gt;Hick’s Law&lt;/a&gt; strikes again. I’m not just talking about looking at a code base. This is the case for popular public design systems too. As much as I admire Adobe’s work on Spectrum, it’s very easy to be intimidated by all of the stuff they have featured in their documentation.&lt;/p&gt;
&lt;p&gt;To illustrate this, I like to use an analogy that I call Storefront &amp;amp; Warehouse.&lt;/p&gt;
&lt;h2 id=&quot;getting-needy&quot;&gt;Getting needy&lt;/h2&gt;
&lt;p&gt;Let’s say you are looking for a new vacuum cleaner. You aren’t picky, you just need something that can clean dirt and pet hair from wood floors on occassion. You walk into a store, find a vacuum that looks good enough, and take it home. It work fine, nothing to be excited about. You got what you paid for and everything is good.&lt;/p&gt;
&lt;p&gt;Now, let’s say you do have some additional criteria for your vacuum. Maybe, you’d prefer that the vacuum was red to match the furnishings, so the appliance blends in better with the room it’ll be stored in. So you walk to the store, and you don’t see any red vacuums on the shelf. You find a stock person and ask if there’s any red vacuums in the back. He comes back and says that the company makes custom plates to change the color. All you need to do is order the plate and your vacuum will be red. So, you walk out with a regular vacuum and change it to be red afterward. You got what you were expecting, even though it took a little bit of extra work and everything is good.&lt;/p&gt;
&lt;p&gt;Now, let’s say you have a very specific desire. You want a vacuum in the shape of a standing lamp, so it really disappears when not in use, or better it can be used as a lamp while standing in the dock. You go to the store and look but there’s no vacuum like that on the shelf. You ask a stock person, if they have any in the back. This stock person loves the idea but knows that there’s no such vacuum in the warehouse. However, the warehouse does have all of the parts to make it. So, the stock person graciously gives you access to the warehouse so that you could build your own. It takes you a while to find all the pieces and figure out how to put them all together. In the end, you finally have precisely the vacuum you’ve been looking for and everything is good.&lt;/p&gt;
&lt;h2 id=&quot;struggling-at-the-store&quot;&gt;Struggling at the store&lt;/h2&gt;
&lt;p&gt;Instead of picturing something walking into a store and looking for a vacuum cleaner, think about it as a peer navigating to your design system resources looking for something they need. In some cases, it’s right there on the shelf. Some times it’s not quite what they need, but with a little configuration, it would be acceptable. And other times, a person would need to scour across different pages to collect the information needed to make exactly what they have in mind.&lt;/p&gt;
&lt;p&gt;There’s increasing struggles in these cases when we think about how we publish about documentation. For example, some documentation sites make you feel like walking directly into the warehouse. You see all the vacuum cleaner pieces but the ones that work off-the-shelf are mixed in with all the parts. It’s not immediately clear where to go for the thing you are supposed to work with. &lt;em&gt;There’s no storefront.&lt;/em&gt; I’d argue many large public design systems are guilty of this problem. With so many options, it’s hard to choose which is the best option as said earlier.&lt;/p&gt;
&lt;p&gt;For folks who find the resource that looks close, sometimes the options aren’t clear. It’s like going to the store and never finding help to let you know that you can customize your vacuum cleaner. Instead, you leave the store and go try another one, or get a vacuum from the shelf and paint it red. In either case, it would have been better if you were informed about the customizability right there on the unit when you first saw it. No need for a stock person to find, the options are clear.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;It also might not be great to have all of the options shown. That can also become overwhelming. Finding the right balance of popular usage and alternative configuration is tricky for sure.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;And finally, for folks that are immediately disappointed because they visit and don’t find anything that they need, this is the hardest to support. And unfortunately, this is something that I believe happens most often. With the rapid iterations of product design, no design system could possibly keep up with specific features. So, it is very common for people visiting resources to not have the time or experience to search for pages of documentation, tinker with some of parts, and craft something that aligns with the system. Often times, people believe it is faster to just make it without using the parts. I think with poorly documented design systems, this might very well be the case.&lt;/p&gt;
&lt;p&gt;Think about it. If the resources in the design system don’t align well with the visitor’s mental model. It’ll take time for that person to read the documentation, get an understanding of the approach and patterns, build something with the parts, and finally integrate with their application. On the other hand, if the person builds it &lt;em&gt;without&lt;/em&gt; adopting a new mental model, they still need to build the feature, it just might be with more individual parts. Importantly, these are parts they are familiar with. It’s that familiarity which equates to the speed of building and meeting the need of the product more quickly.&lt;/p&gt;
&lt;p&gt;That’s why so many resources often align with a person’s mental model instead of more unique presentations, even if that unique presentation is better in any way. It’s why early GUIs speak about a desktop and folders instead of a terminal and directories for the public. It aligns with existing mental models of working in the physical world. This means that introducing something better but novel will be most successful when alongside an existing mental model.&lt;/p&gt;
&lt;p&gt;In other words, if we’re going to offer a way to build the lamp-vacuum, when a person walks into the warehouse it should be immediately clear which parts they’ll need without being totally lost amongst all of the other unncessary parts.&lt;/p&gt;
&lt;h2 id=&quot;search-first&quot;&gt;Search first&lt;/h2&gt;
&lt;p&gt;Years ago, we had to walk through a store to find the place where they kept the vacuums. If I’ve been to the store before, maybe I remember where they’ve been kept. Hopefully, the store didn’t go through a redesign moving the items around, because my mental model of where the items are stored will make it faster for me to get to the thing I need.&lt;/p&gt;
&lt;p&gt;Now, some larger stores like Home Depot have their inventory locations available through their mobile site. You pick the location you are visiting, search for the item at that location, and the website will tell you where that item is in the store by aisle and bin. This way, you can go directly to the item you need without walking through the whole store, learning the layout, and hopefully landing on the thing you needed. This also avoids the possibility of you missing the item through the vast amount of aisles and products. You have confidence what you need is there and where it should be.&lt;/p&gt;
&lt;p&gt;This is why I find the exercise of creating navigation for a documentation site largely useless and potentially even harmful. Finding the thing you want is rarely useful in a wandering scenario. It is more helpful to zero-in on the resource you are looking for by aligning with your mental model. If you are from the UK and you look under “H” for hoover, you wouldn’t find any vacuums because the are listed under “V”. That would make the visitor assume that you have no vacuums because the information architecture doesn’t match their mental model, and &lt;strong&gt;they’ll leave empty handed because of your IA&lt;/strong&gt;. IA that was intended to help makes people leave disappointed.&lt;/p&gt;
&lt;p&gt;Meanwhile, a robust search experience could make the connection internally. Searching for hoover could alias to vacuums and produces the appropriate results. That way, searching could return results from the storefront first and warehouse after.&lt;/p&gt;
&lt;p&gt;This sort of search experience is common for icons, where a person looking for the “close” icon should return for the same result for “x”, “dismiss”, etc.. There are other systems that even let a person &lt;a href=&quot;https://shapecatcher.com/&quot;&gt;&lt;em&gt;draw&lt;/em&gt; the icon to provide results&lt;/a&gt;. We could imagine this expanding to components in our documentation sites.&lt;/p&gt;
&lt;p&gt;The hot take here, drop the exercise of figuring out the IA of your documentation site, and invest time in a robust search. In the world of artificial intelligence, leverage that to match mental models instead of deliberate aliasing. It would be best to meet people where they are, without causing them to visit external sites and learn opinionated IA. But if you don’t have the resources for AI, make search site-wide and fuzzy af. That gives your visitors access to the whole storefront and warehouse, but raises the most appropriate resources for their needs.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>components</category><category>organization</category></item><item><title>Design Attractors</title><link>https://blog.damato.design/posts/design-attractors</link><guid isPermaLink="true">https://blog.damato.design/posts/design-attractors</guid><description>Chaos theory meets design systems.</description><pubDate>Fri, 26 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This week I tuned into &lt;a href=&quot;https://bencallahan.com/the-question&quot;&gt;The Question by Ben Callahan&lt;/a&gt;, co-hosted by &lt;a href=&quot;http://www.artist-developer.com/&quot;&gt;Natalya Shelburne&lt;/a&gt;. During the chat I shared one of my patented hot takes: the design system specialist role will eventually disappear. Of course, if that’s true, it doesn’t bode well for me or for the rest of us in the meeting. Design systems are what we do. The rebuttal in the chat was strong: systems are becoming more complex than ever, and organizations need our expertise to make sense of it all.&lt;/p&gt;
&lt;h2 id=&quot;our-role-today&quot;&gt;Our role today&lt;/h2&gt;
&lt;p&gt;Much of our work today is about &lt;em&gt;agreement&lt;/em&gt; in an organization. In fact, when people ask me to define a design system, I often reply with that single word: agreement. The resources we maintain represent those agreements. A design system maintainer doesn’t really care what color a button is. They care about getting the organization to agree on which color it should be.&lt;/p&gt;
&lt;p&gt;The complexity of our role lies in managing expectations before these agreements are made: the expectations of users who will one day interact with the products, and the expectations of peers who are building the next version of those products. When marketing says they need a carousel, the challenge isn’t the carousel itself, it’s gathering requirements across humans: our users and our peers. That process produces tokens, components, and guidelines. Abstractly, though, those are just agreements.&lt;/p&gt;
&lt;h2 id=&quot;chaos-theory&quot;&gt;Chaos theory&lt;/h2&gt;
&lt;p&gt;From &lt;a href=&quot;https://en.wikipedia.org/wiki/Chaos_theory&quot;&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chaos theory states that within the apparent randomness of chaotic complex systems, there are underlying patterns, interconnection, constant feedback loops, repetition, self-similarity, fractals and self-organization.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I see design systems as subjects of chaos theory. The complexity comes from infinite, undefined human requirements that maintainers must observe and distill into deliverables. But it’s that last word &lt;strong&gt;self-organization&lt;/strong&gt; that feels like the next step.&lt;/p&gt;
&lt;p&gt;The knee-jerk reaction is to say a self-organized design system is impossible. Under traditional methods, that’s probably true. After all, this role exists precisely because someone has to deliberately gather requirements and deliver resources. However, I believe we’ve already answered many of these requirements. Much of the knowledge is common and repeatedly applied. We’ve already settled on many truths about building design systems, whether we recognize it or not.&lt;/p&gt;
&lt;h2 id=&quot;the-perfect-button&quot;&gt;The perfect button&lt;/h2&gt;
&lt;p&gt;What is wrong with this button?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blue-button.jpeg&quot; alt=&quot;Blue button with teal text&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Most practitioners would agree the background and text colors lack sufficient contrast. &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html&quot;&gt;According to the WCAG&lt;/a&gt; the ratio should be at least 4.5:1. We’ve landed on a stable configuration for the relationship between a button’s background and text. We now expect all buttons to meet this contrast ratio in order to be useful.&lt;/p&gt;
&lt;p&gt;This is what I call a &lt;strong&gt;design attractor&lt;/strong&gt;. In &lt;a href=&quot;https://fiveable.me/chaos-theory/unit-3/types-attractors/study-guide/THSUlEJENrwcpgvX&quot;&gt;chaos theory&lt;/a&gt;, an attractor is the stable state toward which a system evolves over time. In design, for a button to be a button, it must also be interactive. This is a design attractor. From there, users expect some visual treatment to distinguish it from non-interactive elements. Exactly what that treatment looks like depends on the local agreements within an organization, but once settled, that’s another design attractor.&lt;/p&gt;
&lt;p&gt;This is why I believe in &lt;a href=&quot;https://bradfrost.com/blog/post/a-global-design-system/&quot;&gt;Brad Frost’s Global Design System&lt;/a&gt; idea. We know what a button is supposed to be and do. So why are we constantly recreating buttons with that knowledge? Functionally, we’ve already converged toward the answers, toward design attractors. These are the stable solutions that emerge as the natural equilibrium of usability, accessibility, and implementation constraints.&lt;/p&gt;
&lt;h2 id=&quot;in-the-world-of-ai&quot;&gt;In the world of AI&lt;/h2&gt;
&lt;p&gt;AI tools today can take a prompt and deliver mediocre user experiences. But how can they even do that? Because they too have developed design attractors, including ones that we haven’t yet defined. They “know” what a button is and can reference those rules to produce another that meets expectations. While they can’t yet replicate the exact button from your specific design system, we’re not far off. We didn’t need to explain the concept of a button to these systems. They figured it out.&lt;/p&gt;
&lt;p&gt;If they can do that, why couldn’t they learn more from additional information? And by “information,” I mean &lt;em&gt;requirements&lt;/em&gt;. Today, AI gathers requirements for individual tasks. Tomorrow, it could gather requirements for entire organizations and deliver outputs that meet shared human expectations.&lt;/p&gt;
&lt;p&gt;The largest argument here is one of &lt;em&gt;expression&lt;/em&gt;. This is why the Global Design System idea is hard to grasp. The button at Home Depot isn’t the same as the button at Visa, or so it seems. In reality, they’re functionally the same button. The difference is in local agreements: Home Depot’s cosmetic style versus Visa’s. Even those stylistic choices still follow the same design attractors. Remember the contrast ratio requirement? Both Home Depot and Visa buttons differ in appearance but follow the same underlying rules.&lt;/p&gt;
&lt;p&gt;I believe there’s a world where AI synthesizes design attractors at scale, allowing a design system to self-organize within an organization. A designer would contribute simply by designing. That contribution would influence the larger system and shift the design attractor slightly. If someone introduced a low-contrast button, it would have little impact against the weight of prior knowledge of higher-contrast buttons. Bad behavior contributes very little; good behavior contributes naturally toward the attractor.&lt;/p&gt;
&lt;p&gt;A person shouldn’t need to look up guidelines, tokens, or components to work. Those resources should be embedded in their context. And even if an experience is claimed to be novel, we’re never truly starting from scratch. Styles themselves would be learned and refined over time, converging toward agreement as the system internalizes how the organization operates.&lt;/p&gt;
&lt;h2 id=&quot;where-are-the-people&quot;&gt;Where are the people?&lt;/h2&gt;
&lt;p&gt;What keeps our roles relevant today is AI’s lack of accuracy. We’ve all seen &lt;a href=&quot;https://www.404media.co/the-software-engineers-paid-to-fix-vibe-coded-messes/&quot;&gt;reports of “vibe-coded” work that humans must revisit&lt;/a&gt;. And with the complexity of managing expectations across design and development, any inaccuracy erodes trust. Lose trust, and the system collapses. Our roles exist because organizations trust us to deliver accurate agreements, no matter how messy the human dynamics.&lt;/p&gt;
&lt;p&gt;But we should be prepared for a future where AI becomes trustworthy enough to take on more of this work. Certainly, there are ethical concerns far beyond design systems. But for now, it’s enough to recognize that design attractors exist and we can either harness them ourselves, or let the machines do it for us.&lt;/p&gt;
&lt;p&gt;What will you decide?&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>design</category><category>artificial intelligence</category><category>organization</category></item><item><title>Late start</title><link>https://blog.damato.design/posts/late-start</link><guid isPermaLink="true">https://blog.damato.design/posts/late-start</guid><description>Begin counting on your terms.</description><pubDate>Thu, 11 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been in the process of writing a book over the past year and the hardest part for me isn’t the writing, it’s the publishing. I’ve changed publishing systems a few times over the course of this work. I’ll most likely have a larger post speaking about the process later, but the current way that I’m expecting to prepare the book for publishing is with CSS. Yep, that’s right. Get your &lt;code&gt;@page&lt;/code&gt; styles ready!&lt;/p&gt;
&lt;p&gt;Getting the options that a more appropriate layout system like InDesign would have isn’t really possible in most browsers today. If I really wanted to lean into all of the features that CSS could have, I’d consider using &lt;a href=&quot;https://www.princexml.com/&quot;&gt;PrinceXML&lt;/a&gt;. Though as it stands right now, I think I’ll have a good book without it.&lt;/p&gt;
&lt;p&gt;That said, there was some really challenging constraints that I had to work through. One of those things was page numbers.&lt;/p&gt;
&lt;h2 id=&quot;meeting-expectations&quot;&gt;Meeting expectations&lt;/h2&gt;
&lt;p&gt;One of the cool things about CSS is the &lt;code&gt;counter()&lt;/code&gt; function. This acts a bit like using state in CSS in limited way. To explain, here’s what using state in React might look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; MyCounter&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; setCount&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;setCount&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(count &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) }&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;            { count }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can effectively do the same thing in CSS. Here’s a basic setup with some comments that explain what each of these relate to:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /* const [count, setCount] = useState(0) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    counter-reset&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; count&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /* setCount(count + 1) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    counter-increment&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; count&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;::marker&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /* (print) count */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    content&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; counter&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(count)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of the bigger differences is that the count increases when an element exists at a selector. I imagine we could get some interesting state machine stuff with selecting values from inputs. But that’s not what we’re after today. Instead, I want to delay the start of the numbers.&lt;/p&gt;
&lt;p&gt;If you open up a technical book, you’ll find that the page numbers don’t start right after your crack open the cover. There’s a few a pages that either don’t have numbers, or have a different counting system like lowercase Roman numerals. Doing this in React would be fairly easy, we can set the start of the counter to a negative number and only print the number if the value is larger than &lt;code&gt;0&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; MyCounter&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; setCount&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;setCount&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(count &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) }&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;            { count &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; count &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In CSS, we don’t have this fine-grained conditional logic just yet. There’s definitely ways to do some &lt;a href=&quot;https://css-tricks.com/using-absolute-value-sign-rounding-and-modulo-in-css-today/&quot;&gt;tricky operations&lt;/a&gt; and maybe there is a way to do it using some of the newer functions, but I found something that’s a little more appropriate when dealing with counters. The new &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@counter-style&quot;&gt;&lt;code&gt;@counter-style&lt;/code&gt;&lt;/a&gt; at-rule.&lt;/p&gt;
&lt;h2 id=&quot;custom-counters&quot;&gt;Custom counters&lt;/h2&gt;
&lt;p&gt;CSS has had keywords that describe the method of counting for a long time. It’s the same way we’d often &lt;em&gt;remove&lt;/em&gt; the style from &lt;code&gt;&amp;lt;ul/&amp;gt;&lt;/code&gt; elements to drop the bullets for other presentations.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    list-style-type&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; none&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For something like a &lt;code&gt;&amp;lt;ol/&amp;gt;&lt;/code&gt; element, the &lt;code&gt;list-style-type&lt;/code&gt; is set as &lt;code&gt;decimal&lt;/code&gt;. You may also know that &lt;code&gt;&amp;lt;ol/&amp;gt;&lt;/code&gt; elements have a special &lt;code&gt;start&lt;/code&gt; property that allows us to start the count at a different number. Immediately, you’d think that we’d use that like our negative &lt;code&gt;setState(-3)&lt;/code&gt; from before. And while you can, it won’t work for my use case. Since I’m using &lt;code&gt;@page&lt;/code&gt; to display page numbers in print media, there’s no “element” for us to apply it to. That’s ok, CSS counters have a syntax that allow us to update the start for a counter and we can make it increment at each page.&lt;/p&gt;
&lt;p&gt;Here, I’m going to start the count at the first page, name the counter &lt;code&gt;page&lt;/code&gt;, and increment at every page:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; :first&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    counter-reset&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; page -3&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    counter-increment&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s a good start, but the problem is that the first page will print the number &lt;code&gt;-3&lt;/code&gt; at the bottom which is not what we’re looking for. Instead, I’d like these to be blank. To do this we’re going to need two &lt;code&gt;@counter-style&lt;/code&gt; declarations:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@counter-style&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; false-start {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    system&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; extends decimal&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    range&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1 infinite&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    fallback&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; blank-only&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@counter-style&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; blank-only {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    system&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; cyclic&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    symbols&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first &lt;code&gt;@counter-style&lt;/code&gt; has the name &lt;code&gt;false-start&lt;/code&gt; which extends the &lt;code&gt;decimal&lt;/code&gt; system which would normally be no different than the default &lt;code&gt;counter&lt;/code&gt;. However, the first key here is that the &lt;code&gt;range&lt;/code&gt; is set to &lt;code&gt;1 infinite&lt;/code&gt; which say to only use positive numbers starting at &lt;code&gt;1&lt;/code&gt;. When using &lt;code&gt;range&lt;/code&gt; you can also use a &lt;code&gt;fallback&lt;/code&gt; which says if the counter produces a number that cannot be written, use this style as a fallback.&lt;/p&gt;
&lt;p&gt;The second &lt;code&gt;@counter-style&lt;/code&gt; uses &lt;code&gt;system: cyclic;&lt;/code&gt; which is normally used to cycle through the given &lt;code&gt;symbols&lt;/code&gt;. In my case, I’ve provided whitespace. This means that when a negative number is passed into the counter, it will print whitespace. This allows me to set an offset and only print numbers starting at 1 exactly where I want them to start. Here’s the whole code using &lt;code&gt;@page&lt;/code&gt;, and a Codepen demonstrating it using &lt;code&gt;&amp;lt;li/&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@counter-style&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; false-start {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    system&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; extends decimal&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    range&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1 infinite&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    fallback&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; blank-only&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@counter-style&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; blank-only {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    system&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; cyclic&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    symbols&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  ;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    counter-increment&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; :first&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    counter-reset&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; page -3&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; :left&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;bottom-left-corner&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        content&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; counter&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; false-start)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; :left&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;bottom-right-corner&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        content&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; counter&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(page&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; false-start)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Late start with @counter-style&quot; src=&quot;https://codepen.io/fauxserious/embed/WbQWdxw?default-tab=html%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/WbQWdxw&quot;&gt;
Late start with @counter-style&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;Sure, my use-case is very specific but maybe there’s someone else out there looking to write a book and not looking to learn a new language to do it.&lt;/p&gt;
&lt;p&gt;CSS is awesome and it could be awesomer. Rachel Andrew has &lt;a href=&quot;https://www.smashingmagazine.com/2015/01/designing-for-print-with-css/&quot;&gt;an article&lt;/a&gt; published over 10 years ago that outlines some of the other cool things that we could have.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; { &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  string-set&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; doctitle content(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;); &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What the hell is this? It’s taking the content of the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; and writing it into the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; on the page! While I have no idea what this means for an accessible web page, this is meant for print and adding the content into the margins of the book. This ability of grabbing HTML content to use in CSS is incredible. It would open up a lot of possibilities (and poor behavior). There’s even a full &lt;code&gt;@footnote&lt;/code&gt; feature in there too.&lt;/p&gt;
&lt;p&gt;Unfortunately, these aren’t in any browsers today. I guess who is printing things anyway now that &lt;a href=&quot;https://www.nbcnews.com/tech/tech-news/federal-judge-rules-copyrighted-books-are-fair-use-ai-training-rcna214766&quot;&gt;the reading is done with AI&lt;/a&gt;? Silly me.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category></item><item><title>My Top 4</title><link>https://blog.damato.design/posts/my-top-4</link><guid isPermaLink="true">https://blog.damato.design/posts/my-top-4</guid><description>If I could only use 4 CSS properties, what would they be?</description><pubDate>Fri, 15 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For a &lt;a href=&quot;https://www.youtube.com/watch?v=kvf33puDRuw&quot;&gt;recent episode of wireframe&lt;/a&gt;, I borrowed some questions from another person’s AMA. Overall, good questions but one of them stuck out to me where I wanted to give it more thought:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You are asked to build a website but you can use only 4 CSS properties, what are those?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It’s clear to me that you’d have to think strategically about this. While I’d say that something like &lt;code&gt;display&lt;/code&gt; is super powerful, it also often requires additional properties to make things behave afterward. So, the key is to think of properties that stand on their own and can have some significant impact once used. Additionally, it’s pretty clear what we’re going to be working on is a Craiglist-like design since I can’t use a lot of properties for aesthetic purposes.&lt;/p&gt;
&lt;p&gt;Without further ado, here’s my picks:&lt;/p&gt;
&lt;h2 id=&quot;overflow-wrap&quot;&gt;&lt;code&gt;overflow-wrap&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This might be a property you aren’t using but I truly believe it should be in &lt;a href=&quot;https://www.joshwcomeau.com/css/custom-css-reset/&quot;&gt;people’s resets&lt;/a&gt;. Specifically &lt;code&gt;overflow-wrap: break-word&lt;/code&gt;. This ensures that long text such as URLs don’t break out of their containers. While it’s possible you might not need it on your site today, you’ll probably want to have it for the future.&lt;/p&gt;
&lt;h2 id=&quot;max-width&quot;&gt;&lt;code&gt;max-width&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The prime example I’m thinking of is &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements rendering as larger than their container. Setting &lt;code&gt;max-width: 100%&lt;/code&gt; on these, along with other similar elements will help ensure that they don’t break layouts. This would also be helpful to keep long lines of text from occuring for readability. Even if the content itself might not be centered nicely on the page without something like &lt;code&gt;margin-inline: auto&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;margin&quot;&gt;&lt;code&gt;margin&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Speaking of &lt;code&gt;margin&lt;/code&gt;, I’m adding it to my list. Between the ability to horizontally center content and some of the egregious values set in browser defaults, having this will really clean up the overall hierarchy and presentation of a page. The use of the shorthand values will help here so that I can get horizontal centering with &lt;code&gt;margin: 0 auto&lt;/code&gt; and fix defaults with &lt;code&gt;margin: 0 0 8px&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;scroll-behavior&quot;&gt;&lt;code&gt;scroll-behavior&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The reason I’m adding this, specifically &lt;code&gt;scroll-behavior: smooth&lt;/code&gt;, is for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Hash_routing&quot;&gt;hash routing&lt;/a&gt; on links. For example, when I’m reading &lt;a href=&quot;https://drafts.csswg.org/&quot;&gt;working group specifications&lt;/a&gt; and click a link, it often jumps somewhere making it really hard to know if I was navigated to another page or where this next section is relative to the old section I was just looking at. Smoothly scrolling after clicking the link makes the relationship more clear. Importantly, this would need to be inside of a &lt;code&gt;@media (prefers-reduced-motion)&lt;/code&gt; rule to be ignored for folks who are sensitive to motion. So, it’s possible my fourth rule could be wasted because of this.&lt;/p&gt;
&lt;p&gt;I made a quick sample page to demonstrate the usage.&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Untitled&quot; src=&quot;https://codepen.io/fauxserious/embed/ByoJdLd?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/ByoJdLd&quot;&gt;
Untitled&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;What are your top 4? 🤔&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category></item><item><title>Antidatalossconfigurationism</title><link>https://blog.damato.design/posts/antidatalossconfigurationism</link><guid isPermaLink="true">https://blog.damato.design/posts/antidatalossconfigurationism</guid><description>Wrangling text and the CSS behind it all.</description><pubDate>Tue, 08 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m in the middle of deep thought about what a text component looks like. There’s the easy stuff like having the ability to choose the appropriate semantic element. There’s the less obvious stuff, like adding a flag for &lt;a href=&quot;/posts/screenreader-only/&quot;&gt;screenreader only content&lt;/a&gt;. But then there’s something that we tend to overlook, how to manage overflow content.&lt;/p&gt;
&lt;h2 id=&quot;data-loss&quot;&gt;Data loss&lt;/h2&gt;
&lt;p&gt;It’s not uncommon for someone to choose the solution of clipping the content, maybe in the form of an ellipsis. We even have more options coming about &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/line-clamp&quot;&gt;when those ellipsis appear&lt;/a&gt;. The problem with approaches that include &lt;code&gt;overflow: hidden;&lt;/code&gt; is that the content is no longer clearly accessible. This goes against a core principle of mine: if you place it on the page, that means it is important. There should be no condition that obstructs that element in any way. Otherwise that can introduce data loss. Adding ellipsis is not an inclusive solution to large blocks of text. Instead, we’ll need to ensure the text remains readable while balancing the expectations of the layout.&lt;/p&gt;
&lt;h2 id=&quot;properties&quot;&gt;Properties&lt;/h2&gt;
&lt;p&gt;There’s several CSS properties that are directly related to the way text flows on a webpage. We’ll go over each of the properties below but first a few definitions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Whitespace&lt;/strong&gt; refers to characters that provide space between other characters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Segment breaks&lt;/strong&gt; are characters that cause text to break onto new lines (eg., line feed).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hard breaks&lt;/strong&gt; (aka: forced line break) will always cause the text to break onto a new line, even if it is not necessary to do so.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Soft breaks&lt;/strong&gt; will cause text to break to a new line only if necessary. Where these can occur are called soft wrap opportunities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Line box&lt;/strong&gt; describes the inline formatted box, typically used to contain text. The size of this box depends on the content and the available size of its ancestors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CJK&lt;/strong&gt; is short for Chinese/Japanese/Korean used to describe text that has a different ruleset for when strings are allowed to break.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;white-space&quot;&gt;&lt;code&gt;white-space&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This property has been around for a while, but recently it has been altered to host separate properties. Overall, this property and its related new properties are meant to handle how whitespace characters effect the surrounding text.&lt;/p&gt;
&lt;p&gt;First, the &lt;code&gt;white-space-collapse&lt;/code&gt; property has the following possible values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;collapse&lt;/code&gt; says that all whitespace should be collapsed. How whitespace is collapsed &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/white-space-collapse#collapsing_of_white_space&quot;&gt;follows several algorithmic steps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preserve&lt;/code&gt; says whitespace and segment break characters are perserved. This is commonly seen as the default in the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/pre&quot;&gt;&lt;code&gt;&amp;lt;pre/&amp;gt;&lt;/code&gt; HTML element&lt;/a&gt; style.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preserve-breaks&lt;/code&gt; says only preserve segment breaks, whitespace characters are collapsed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preserve-spaces&lt;/code&gt; does the opposite of &lt;code&gt;preserve-breaks&lt;/code&gt; by allowing segment breaks to be collapsed and perserve whitespace as written in source.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;break-spaces&lt;/code&gt; is the most unique of the values with a more specific set of rules for how whitespace is perserved.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember that &lt;code&gt;white-space-collapse&lt;/code&gt; is only one part of the &lt;code&gt;white-space&lt;/code&gt; property. The other part is &lt;code&gt;text-wrap-mode&lt;/code&gt; which only has two values, &lt;code&gt;wrap&lt;/code&gt; and &lt;code&gt;nowrap&lt;/code&gt;. As you might expect, you’ll primarly want to have &lt;code&gt;text-wrap-mode: wrap&lt;/code&gt; and this is the default.&lt;/p&gt;
&lt;p&gt;When considering the keyword options provided by the original &lt;code&gt;white-space&lt;/code&gt; property, it’s not immediately clear how they align to these values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;normal&lt;/code&gt; is &lt;code&gt;white-space-collapse: collapse; text-wrap-mode: wrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pre&lt;/code&gt; is &lt;code&gt;white-space-collapse: preserve; text-wrap-mode: nowrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pre-line&lt;/code&gt; is &lt;code&gt;white-space-collapse: preserve-breaks; text-wrap-mode: wrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pre-wrap&lt;/code&gt; is &lt;code&gt;white-space-collapse: preserve; text-wrap-mode: wrap&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;There is no keyword analog under &lt;code&gt;white-space&lt;/code&gt; to get &lt;code&gt;white-space-collapse: preserve-spaces&lt;/code&gt; behavior. At the time of this writing, this is only available in Firefox.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h3 id=&quot;text-wrap&quot;&gt;&lt;code&gt;text-wrap&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To make things more confusing, &lt;code&gt;text-wrap&lt;/code&gt; is also a shorthand. We already saw one of the properties &lt;code&gt;text-wrap-mode&lt;/code&gt; which determines if the text is allowed to wrap or not. The newer part to this is &lt;code&gt;text-wrap-style&lt;/code&gt; which has added many of the fancy algorithmic ways that text can wrap based on the number of characters. Here are the values for this property:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt; wraps the text in the most performant way and ignores the number of characters. This is the default.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;balance&lt;/code&gt; attempts to wrap in away that keeps the number of characters roughly equal amongst the lines of text. This is best used for headlines.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pretty&lt;/code&gt; is similar to balance but favors layout over speed. This is best used for paragraphs of text.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stable&lt;/code&gt; is meant to be applied in areas where the user is editing content and wrapping should be kept to a minimum during this time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://csswizardry.com/&quot;&gt;Harry Roberts&lt;/a&gt; &lt;a href=&quot;https://x.com/csswizardry/status/1725543814587134435&quot;&gt;posted some benchmarks&lt;/a&gt; on using the newer &lt;code&gt;text-wrap&lt;/code&gt; values. The setup was a page with 10,000 &lt;code&gt;&amp;lt;p/&amp;gt;&lt;/code&gt; elements which, by default, have orphans. Here’s what he found:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Baseline – 1,186ms&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text-wrap: balance&lt;/code&gt;: 1,224ms (38ms/3.2% longer)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text-wrap: pretty&lt;/code&gt;: 1,310ms (124ms/10.5% longer)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;He said that impact on &lt;code&gt;balance&lt;/code&gt; was almost zero, while &lt;code&gt;pretty&lt;/code&gt; is more noticeable. The reality is that you most likely won’t have 10,000 paragraphs on your page.&lt;/p&gt;
&lt;p&gt;The properties so far are only configuring how the whitespace characters behave in a block of text. None of these properties will affect the strings of characters that typically behave as words. The following properties are meant to target when these strings are meant to break and avoid poor layout artifacts.&lt;/p&gt;
&lt;h3 id=&quot;overflow-wrap&quot;&gt;&lt;code&gt;overflow-wrap&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;overflow-wrap&lt;/code&gt; property helps tell the browser when it should insert line breaks in otherwise unbreakable strings of text to prevent overflowing. This is an alias of the &lt;code&gt;word-wrap&lt;/code&gt; property. Originally, this only allowed two values &lt;code&gt;normal&lt;/code&gt; the default which keeps strings in tact and &lt;code&gt;break-word&lt;/code&gt; which will break words &lt;em&gt;sometimes&lt;/em&gt;. When a word is meant to break depends on the width of the container and where soft breaks can exist. This is usually very helpful for places where a long URL is written that needs to be broken to maintain a reasonable text layout.&lt;/p&gt;
&lt;p&gt;The newer value option is &lt;code&gt;anywhere&lt;/code&gt; which is typically less desirable as soft wrap characters are also used to determine how big the line box should be. This will allow the box to be much smaller instead of trying to fill the box as is normally expected.&lt;/p&gt;
&lt;h3 id=&quot;word-break&quot;&gt;&lt;code&gt;word-break&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Continuing with the theme of confusingly similar CSS properties, &lt;code&gt;word-break&lt;/code&gt; is a property that determines what happens when a string would overflow outside of its container. To make things worse, one of the values here is an override to &lt;code&gt;overflow-wrap&lt;/code&gt;. We’ll start with that one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;break-word&lt;/code&gt; is identical to &lt;code&gt;overflow-wrap: anywhere&lt;/code&gt; and takes priority over any other value provided to &lt;code&gt;overflow-wrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;normal&lt;/code&gt; says to use normal line break rules which may also break between CJK characters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;break-all&lt;/code&gt; will cause a break to occur at the place where overflow would occur.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;keep-all&lt;/code&gt; same as &lt;code&gt;normal&lt;/code&gt; except CJK characters will also not break.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto-phrase&lt;/code&gt; same as &lt;code&gt;normal&lt;/code&gt; but analysizes the language to avoid breaking natural phrases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A question you might have, what’s the difference between all of these properties? They all look like they handle the same sort of consideration for breaking strings of text. The CSSWG has an &lt;a href=&quot;https://drafts.csswg.org/css-text/#line-breaking&quot;&gt;excellent breakdown of the differences&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;line-break&lt;/code&gt; property allows choosing various levels of “strictness” for line breaking restrictions.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;word-break&lt;/code&gt; property controls what types of letters are glommed together to form unbreakable “words”, causing CJK characters to behave like non-CJK text or vice versa.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;hyphens&lt;/code&gt; property controls whether automatic hyphenation is allowed to break words in scripts that hyphenate.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;overflow-wrap&lt;/code&gt; property allows the user-agent to take a break anywhere in otherwise-unbreakable strings that would otherwise overflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;On top of the other properties that we didn’t cover, there’s a lot more detail within that page about how line breaks are expected to work. I’ve made a little playground to see how the properties would affect text samples combined from MDN examples:&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Antidatalossconfigurationism&quot; src=&quot;https://codepen.io/fauxserious/embed/jEbNMge?result&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/jEbNMge&quot;&gt;
Antidatalossconfigurationism&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;recommendations&quot;&gt;Recommendations&lt;/h2&gt;
&lt;p&gt;Based on all of this, here’s how I’d try making an API for a text component when it comes to avoiding data loss. Here’s my considerations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;wrap&lt;/code&gt; flag with the following values:
&lt;ul&gt;
&lt;li&gt;(default) if the tag name used expects a certain &lt;code&gt;text-wrap-style&lt;/code&gt; value, use that style. For example, &lt;code&gt;balance&lt;/code&gt; for shorter text and &lt;code&gt;pretty&lt;/code&gt; for longer text.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt; is the same as &lt;code&gt;text-wrap-mode: wrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt; is the same as &lt;code&gt;text-wrap-mode: nowrap&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;break&lt;/code&gt; flag with the following values:
&lt;ul&gt;
&lt;li&gt;(default) sets &lt;code&gt;overflow-wrap: break-word&lt;/code&gt; on text by default. This will allow the presence of long URL to break nicely when within the text. &lt;a href=&quot;https://www.joshwcomeau.com/&quot;&gt;Josh Comeau&lt;/a&gt; includes this in &lt;a href=&quot;https://www.joshwcomeau.com/css/custom-css-reset/&quot;&gt;his CSS reset&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;true&lt;/code&gt; is the same as &lt;code&gt;word-break: break-all&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;false&lt;/code&gt; is the same as &lt;code&gt;word-break: keep-all&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s what the CSS might look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    overflow&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; visible &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;!important&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    overflow-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; break-word&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;where(h1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; h2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; h3&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; h4&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; h5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; h6&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; dt&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; legend&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; th&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        text-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; balance&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; li&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; figcaption&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; dd&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; summary&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; caption&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        text-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; pretty&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;textarea&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;contenteditable&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        text-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; stable&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;pre&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; kbd&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        text-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; pre-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        text-wrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; nowrap&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-break&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        word-break&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; break-all&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-break&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        word-break&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; keep-all&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m using the keyword value for &lt;code&gt;text-wrap&lt;/code&gt; over the shorthand because it’s easier to update this value instead of unsetting it to reapply in some cases. This allows us to leverage the default of &lt;code&gt;text-wrap-mode: wrap&lt;/code&gt; for most elements.&lt;/p&gt;
&lt;p&gt;The elements I’ve chosen to receive &lt;code&gt;text-wrap-style&lt;/code&gt; are based on their expected content. Elements that expect shorter amounts of content receive &lt;code&gt;balance&lt;/code&gt; while elements that expect longer amounts use &lt;code&gt;pretty&lt;/code&gt;. Note that these are targeting elements that would normally have content as their direct children. While you could put &lt;code&gt;pretty&lt;/code&gt; on sectioning content for it to cascade, it’s more performant to be more specific.&lt;/p&gt;
&lt;p&gt;If you were paying attention, you’ll notice that there’s a lot of property values that we went over which are missing from the API. The reason is that they’re probably not going to be useful in day-to-day web development. This isn’t meant to be a CSS reset, this is an opinionated setup for what I believe would be a text component that allows for some customizability while keeping the options limited.&lt;/p&gt;
&lt;p&gt;This includes my own rule-breaking for use of &lt;code&gt;!important&lt;/code&gt; in this case to call out that hiding overflow on text is a bad practice. &lt;a href=&quot;/posts/out-of-order/&quot;&gt;Makes me want to set &lt;code&gt;z-index: auto !important&lt;/code&gt;&lt;/a&gt; on &lt;a href=&quot;/posts/hottest-box/&quot;&gt;all my elements&lt;/a&gt; too.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>typography</category><category>css</category></item><item><title>Font size dimensions</title><link>https://blog.damato.design/posts/font-size-dimensions</link><guid isPermaLink="true">https://blog.damato.design/posts/font-size-dimensions</guid><description>Pixels or font size for dimensions, and when.</description><pubDate>Fri, 20 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;First, we used pixels to describe the size of things on the web. Most digital tools and technologies communicated in pixels so this seemed like a logical decision to use. Over time, we’ve received other units in CSS that were relative to some other amount. Percentages were based on the parent element and many other units were based on font size.&lt;/p&gt;
&lt;p&gt;The use of font size was highly misunderstood across our practice for years. One of the earliest concepts was converting &lt;code&gt;(r)em&lt;/code&gt; to pixels since many people weren’t familiar with the new units, but saw benefits to using &lt;code&gt;(r)em&lt;/code&gt; in other areas outside of updating the size of the font. Unfortunately, this eventually turned into &lt;a href=&quot;/posts/62-5&quot;&gt;the bad practice of setting &lt;code&gt;font-size: 62.5%&lt;/code&gt; on the &lt;code&gt;html&lt;/code&gt; element&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;space&quot;&gt;Space&lt;/h2&gt;
&lt;p&gt;In my later work, I did adopt the concept of using &lt;code&gt;(r)em&lt;/code&gt; to define elements outside of font size, specifically space. This looked promising since areas that had larger typography also typically have larger spacing. However, I’ve more recently walked that back. This is because of accessibility.&lt;/p&gt;
&lt;p&gt;If we decide to use font relative units to define space and a user decides to increase the font size of the text, this would also increase the amount of space between elements. This would make the overall composition very large. I realized this while exploring &lt;a href=&quot;/posts/complementary-space/&quot;&gt;Complementary Space&lt;/a&gt;, because I could separate what space meant at different regions. It made more sense to keep the amount of space effectively static (using pixels) between elements as long as the perceived hierarchy was maintained. As an example, as long as the space found in the hero section was larger in relation to the space found in table cells, the text could be sized up for legibility while maintaining the essence of hierarchy. This would keep the overall layout smaller while allowing the text to be as large as the user needed.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;At the time of this writing, this blog uses relative font units for space. You can see my change of perspective is very recent. If you see the source and it isn’t using relative font units, then I found time to update it.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;width&quot;&gt;Width&lt;/h2&gt;
&lt;p&gt;Another place where people tend to try using relative units is for defining the width, either explicitly or as a limit. One of my principles for composition layout is to &lt;em&gt;not&lt;/em&gt; define widths and let the content dictate the size of its container. This isn’t always possible. One common example is when using &lt;code&gt;display: grid&lt;/code&gt;. I tend to use the &lt;code&gt;repeat(auto-fit, minmax())&lt;/code&gt; technique to define columns dynamically and this requires some dimension for the minimum or maximum. Most of the time I set this as &lt;code&gt;minmax(260px, 1fr)&lt;/code&gt; for card containing layouts and the &lt;code&gt;260px&lt;/code&gt; has very much been a LooksGood™ value that I didn’t put much thought into.&lt;/p&gt;
&lt;p&gt;So are relative font units helpful or hurtful when defining width? To get an answer we need to understand why we are setting a value for the width at all. Generally, we set a width on an element so it maintains an expected size, but why? As a thought experiment, we can consider an element primarily made up of text, we want the text to have an optimal reading composition. Lines that aren’t too long but also lines that aren’t too short. This also depends on the kind of content. Optimal line length for long form articles is around 60 characters, give or take. We can use that number to set a maximum width for the article; &lt;code&gt;60ch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We don’t need to go too much further to see that the number of characters is something we take into account when speaking about the width of an element. We can imagine this in the opposite direction, if the element is too small, the lines look smooshed. Here’s where we find the key insight. If we defined the width of an element using relative font units, and the user increases the font size, what happens? Think about it.&lt;/p&gt;
&lt;p&gt;Let’s use &lt;code&gt;30ch&lt;/code&gt; as a maximum width, which is approximately the &lt;code&gt;260px&lt;/code&gt; value from earlier, not counting the card padding using common font metrics for the body. As the user increases the font size, what &lt;code&gt;30ch&lt;/code&gt; means will &lt;em&gt;also increase&lt;/em&gt; as pixels. This means that the 30 characters will continue to be 30 characters, just larger in pixels. Visually, this would look like if we scaled the whole element up. This maintains the overall composition as the font size increases.&lt;/p&gt;
&lt;p&gt;On the other hand, if we used a &lt;code&gt;260px&lt;/code&gt; maximum width, the sheer size of large text would wrap too quickly, causing premature line wrapping because the pixels aren’t increasing with the size of the text. For this reason, I now recommend using relative font sizes, more specifically &lt;code&gt;(r)ch&lt;/code&gt; units, to define widths. That is, if you truly need to define a width at all. For me, the best case is to still avoid setting a width wherever possible and let the content dictate the size.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;Importantly, things about this are slightly different between large desktops and small touch screens. The expectation for larger screens is to reflow first before overflowing outside the viewport. It is reasonable for overflow to occur sooner on smaller devices due to the limited space but similar font size we’d expect a user to have between large and small devices.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;breakpoints&quot;&gt;Breakpoints&lt;/h2&gt;
&lt;p&gt;The reason that I had the idea for this article is due to &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7341362642747387905/&quot;&gt;this post on LinkedIn&lt;/a&gt; by Matthew Wood. He explains the advantage of using &lt;code&gt;(r)em&lt;/code&gt; for media and container queries. He outlines how the approach helps support specific articles within the WCAG, &lt;a href=&quot;https://yatil.net/blog/resize-text-reflow&quot;&gt;1.4.4 and 1.4.10&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Due to the common use of minimum and maximum widths in breakpoints, and our earlier exploration into the benefits of maintaining layout composition when using relative font units for this, I can agree that using relative font units for breakpoints would be preferred over pixel values. That is, if you need breakpoints at all.&lt;/p&gt;
&lt;p&gt;As I wrote years ago within &lt;a href=&quot;https://gridless.design/&quot;&gt;Gridless Design&lt;/a&gt;, the web was made to flow like a document. So if something doesn’t fit, the best way to handle it is to let the element wrap underneath. The more custom you want that behavior to be, the more difficult it is to appropriately handle how it is meant to behave for different user preferences and devices.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;In this article’s alternate reality, we’d discuss how defining &lt;code&gt;font-size&lt;/code&gt; should include fixed pixel amounts when working with fluid typography to observe the browser’s zoom feature. This is outlined in detail within &lt;a href=&quot;https://www.smashingmagazine.com/2023/11/addressing-accessibility-concerns-fluid-type/&quot;&gt;this article on Smashing Magazine&lt;/a&gt; by Maxwell Barvian.&lt;/p&gt;&lt;p&gt;And avoid setting the root font size. Let the user provide that, please.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;These are my new rules of thumb based on the earlier explorations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use fixed pixel units (&lt;code&gt;px&lt;/code&gt;) for spacing.&lt;/li&gt;
&lt;li&gt;Avoid setting dimensions wherever possible.&lt;/li&gt;
&lt;li&gt;If you must set a dimension, use font relative units (&lt;code&gt;(r)ch&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wouldn’t consider these rigid, but I would refrain from deviating unless you truly understand why.&lt;/p&gt;
&lt;p&gt;And for heavens sake &lt;strong&gt;don’t make tokens for dimensions&lt;/strong&gt;. Dimensions are specific to the layout. This includes breakpoints because when the layout breaks isn’t due to being a specific device. It’s because the current composition visually &lt;em&gt;breaks&lt;/em&gt; at this &lt;em&gt;point&lt;/em&gt;. Unless you’re making tokens to define the parts of layout, avoid making tokens for dimensions.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>accessibility</category><category>typography</category><category>space</category><category>css</category></item><item><title>Accent Color</title><link>https://blog.damato.design/posts/accent-color</link><guid isPermaLink="true">https://blog.damato.design/posts/accent-color</guid><description>Personalization causes privacy concerns amongst engineers.</description><pubDate>Tue, 10 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The concept of an accent color occurs in modern web design often, especially in minimalistic compositions of black and white. The accent color is typically used to start drawing attention to important elements and tie in with the beginnings of a brand expression. We often see this manifest in rudimentary token systems that might include something like &lt;code&gt;$accent-color&lt;/code&gt; such that this color can be changed to affect an overall mood where appropriate.&lt;/p&gt;
&lt;p&gt;In fact, this very blog uses this approach on each of the articles to indicate how spicy a particular topic is. This is an easy way to keep the overall look of a site consistent across pages, while giving individual pages their own personality.&lt;/p&gt;
&lt;h2 id=&quot;accent-color&quot;&gt;&lt;code&gt;accent-color&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Recently, we received a CSS property called &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/accent-color&quot;&gt;&lt;code&gt;accent-color&lt;/code&gt;&lt;/a&gt;, which is made to affect the color provided by the user-agent and operating system with one provided by the brand through a stylesheet:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;radio&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;range&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;progress&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    accent-color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; hotpink&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will update the color of some user-interface controls to &lt;code&gt;hotpink&lt;/code&gt; in browsers that support it. This helps convey a consistent brand easily without needing to provide customized controls which might require a lot of reimplementation. Unfortunately, due to the spotty support, teams continue to resort to custom controls to properly convey their brand with custom colors.&lt;/p&gt;
&lt;h2 id=&quot;accentcolor&quot;&gt;&lt;code&gt;AccentColor&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Where &lt;code&gt;accent-color&lt;/code&gt; is a method for a web designer to &lt;em&gt;set&lt;/em&gt; a color for user-interface controls, the &lt;code&gt;AccentColor&lt;/code&gt; keyword is a method for a web designer to &lt;em&gt;get&lt;/em&gt; the current color given to user interface controls… &lt;a href=&quot;#fingerprinting&quot;&gt;sort of&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/system-color&quot;&gt;the MDN page for system color&lt;/a&gt;, you’ll find other keywords on the page that are meant to provide default color choices for a web page. As an example, the &lt;code&gt;VisitedText&lt;/code&gt; is the purple we’d see as the default color for visited links.&lt;/p&gt;
&lt;p&gt;If you scroll to the bottom of MDN for the Browser compatibility table, you might find something strange:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/browser-compat-accent-color.png&quot; alt=&quot;Browser compatibility table for system color&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AccentColor&lt;/code&gt; (and &lt;code&gt;AccentColorText&lt;/code&gt;) are called out as not supported in many browsers in comparison to the rest of the list. What’s up with that? The answer: &lt;strong&gt;Privacy&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;fingerprinting&quot;&gt;Fingerprinting&lt;/h2&gt;
&lt;p&gt;The introduction of &lt;code&gt;AccentColor&lt;/code&gt; into browsers begins &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/7347&quot;&gt;here&lt;/a&gt; in 2022 and everything is going fairly smoothly until August 2023, where Joey Arhar, a engineer working on the Chrome browser for Google, leaves a comment suggesting that implementing this feature could present a new “fingerprinting” vector. This spawns a totally new and separate discussion about &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/10372&quot;&gt;Mitigating fingerprinting for AccentColor/AccentColorText&lt;/a&gt; bringing the original discussion to a halt over these concerns.&lt;/p&gt;
&lt;p&gt;So what’s all of this about fingerprinting? &lt;a href=&quot;https://web.dev/learn/privacy/fingerprinting&quot;&gt;Here’s what web.dev says&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fingerprinting means trying to identify a user when they return to your website, or identifying the same user across different websites.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea is that identifying the color assigned to &lt;code&gt;AccentColor&lt;/code&gt; for your device could be a data point in a larger collection that would specifically identify you (or more specifically your device) such that scripts could follow you from site to site. You might see how this could be used from an analytics point-of-view, where you could follow the path of a user from an advertisement all the way to a purchase flow. However, this could also be used in a digital surveillence capacity. Scripts could collect data about a target’s device and then identify how that device travels between collaborative or infected sites.&lt;/p&gt;
&lt;p&gt;This is the reason some of the properties that were once introduced at &lt;code&gt;window.navigator&lt;/code&gt; are now marked as deprecated. It is because they offered too much information that could be used for fingerprinting. If your code is depending on the type of browser or device, it’s possible this is no longer reliable. Instead, you should modify your code to use feature detection instead.&lt;/p&gt;
&lt;p&gt;Importantly, the real problem isn’t the color but the &lt;em&gt;value&lt;/em&gt; that the color represents. There’s no problem with displaying the color to the user. The problem arises when some code attempts to read the value of the given color which could be used as a data point. That’s why the newer discussion considers returning static values for when the color is meant to be retrieved, such as using &lt;code&gt;getComputedStyle()&lt;/code&gt; in JavaScript or if the color was to be rasterized to an HTML &lt;code&gt;&amp;lt;canvas/&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What isn’t clear yet is how the keyword might work with CSS &lt;code&gt;color-mix()&lt;/code&gt; and this is why I’m heavily interested in the outcome.&lt;/p&gt;
&lt;h2 id=&quot;personalization&quot;&gt;Personalization&lt;/h2&gt;
&lt;p&gt;In my most recent work, I speak about the concept of &lt;strong&gt;influence&lt;/strong&gt;. This means instead of an organization curating tokens precisely, I recommend looking into the idea of influencing existing values with some brand expression. In this way, we don’t specifically say that &lt;code&gt;$body-bg&lt;/code&gt; is exactly &lt;code&gt;#ffffff&lt;/code&gt; but instead we &lt;em&gt;influence&lt;/em&gt; the given color with ours by some amount. What is the given color? In my opinion, it’s the color chosen by the user. How it is chosen can be a separate topic altogether but consider this; imagine if we could support light and dark mode simply by saying “&lt;code&gt;#66ff22&lt;/code&gt; is our brand color” and let the system figure out the rest.&lt;/p&gt;
&lt;p&gt;Here’s a practical example: let’s say I’m trying to convey the brand of Home Depot with their signature orange &lt;code&gt;#ee7125&lt;/code&gt;. Instead of going through all of the tokens, for both light and dark mode, what if instead I only provide this one color and let modern color functions and system colors fill in the rest?&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /* Set our brand color */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --brand-color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; #ee7125&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /* This is needed for system colors to work */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    color-scheme&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; light dark&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /* Use color-mix to influence the system color */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    background&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; color-mix&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(in oklch&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; Canvas&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--brand-color) 5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; color-mix&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(in oklch&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; CanvasText&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--brand-color) 5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would tint the background and foreground colors with the &lt;code&gt;--brand-color&lt;/code&gt; by &lt;code&gt;5%&lt;/code&gt;. Importantly, the color supplied by &lt;code&gt;Canvas&lt;/code&gt; and &lt;code&gt;CanvasText&lt;/code&gt; is supplied by the device and browser depending on the user’s settings. As an example, in light mode, we’d expect &lt;code&gt;Canvas&lt;/code&gt; to be a light color. Importantly, you cannot be sure what this color keyword is exactly. As a reiteration, this means we aren’t choosing the color for the background and foreground directly, but we are influencing what is given by the user’s choices with our own orange brand.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;The &lt;code&gt;5%&lt;/code&gt; used above is just used for example purposes. In reality I’d figure &lt;code&gt;Canvas&lt;/code&gt; and &lt;code&gt;CanvasText&lt;/code&gt; wouldn’t need brand influence at all. Instead, I’d consider adjusting actions and controls with some brand influence as they are meant to stand out more than most surfaces. Which system colors to use for those is less straightforward.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;Ultimately, I’d like to see system colors that were closer to the concept of &lt;a href=&quot;/posts/tokens-as-intents/&quot;&gt;intents&lt;/a&gt;. Though I’ve found some sensible replacements in most cases. You can see some of this in action at &lt;a href=&quot;https://system.damato.design&quot;&gt;my system playground&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There’s lots of opporunity for this to fail unfortunately but I believe there’s also opportunity for this to succeed. A user can be responsible for the appropriate general presentation either actively (&lt;a href=&quot;https://blog.codinghorror.com/a-tribute-to-the-windows-31-hot-dog-stand-color-scheme/&quot;&gt;heyo hot dog stand&lt;/a&gt; 🌭) or passively (&lt;a href=&quot;https://www.nngroup.com/articles/generative-ui/&quot;&gt;maybe learning user activity&lt;/a&gt;). While we influence that presentation slightly with our own branding. This needs more experts, especially ones who work with color, to figure out best practices over time.&lt;/p&gt;
&lt;p&gt;Folks that are used to providing highly curated values for their experiences are going to feel uneasy with this. But for those who truly put the user first, we can &lt;a href=&quot;/posts/colors-dont-solve-problems/&quot;&gt;lessen the emphasis on choosing color&lt;/a&gt; and redirect our efforts to providing a better overall experience.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>color</category><category>css</category><category>inclusive design</category></item><item><title>CSS Only Contrast</title><link>https://blog.damato.design/posts/css-only-contrast</link><guid isPermaLink="true">https://blog.damato.design/posts/css-only-contrast</guid><description>We&apos;ve always wanted it, now it looks like we can have it.</description><pubDate>Wed, 04 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I think many of us who work with CSS and accessibility have been looking for a solution that helps provide the right amount of contrast for a given color. It’s floated around as a function within different CSS color specifications, currently found as &lt;a href=&quot;https://drafts.csswg.org/css-color-5/#contrast-color&quot;&gt;&lt;code&gt;contrast-color()&lt;/code&gt; in CSS Color Module Level 5&lt;/a&gt; with the following syntax:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;background: var(--color);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;color: contrast-color(var(--color));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, this isn’t available in any browser and there’s no references found for it in MDN or caniuse. So, do we just keep waiting? Maybe not!&lt;/p&gt;
&lt;h2 id=&quot;prior-art&quot;&gt;Prior art&lt;/h2&gt;
&lt;p&gt;Over 5 years ago, there was &lt;a href=&quot;https://css-tricks.com/css-variables-calc-rgb-enforcing-high-contrast-colors/&quot;&gt;a post on CSS Tricks&lt;/a&gt; that began to poke at this problem. &lt;a href=&quot;https://joshuabader.com/&quot;&gt;Josh Bader&lt;/a&gt; outlined a method that takes the channels of an RGB color and combines the values into a luminance value to be reassigned into a new RGB color.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --red&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 28&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --green&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 150&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --blue&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 130&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --accessible-channel&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;      (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;          (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--red) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 299) +&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;          (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--green) * 587&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) +&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;          (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(--blue) * 114)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        ) / 1000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;      ) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 128&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    ) * &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;1000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --accessible-color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; rgb&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--accessible-channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--accessible-channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--accessible-channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;I’ve updated the example a bit to demonstrate that the calculations are trying to determine the channels for the color, not the color directly.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;The problem with this approach is that you needed to know the amount of each channel such that it could be combined together. This made it tedious to define a color because it’s uncommon to have to set color channels individually, especially when we begin to use variables and tokens which are curated by folks that aren’t close to the code.&lt;/p&gt;
&lt;p&gt;Luckily, we do have some modern CSS that can help with this and get us much closer to a good developer experience with only CSS.&lt;/p&gt;
&lt;h2 id=&quot;relative-color-syntax&quot;&gt;Relative color syntax&lt;/h2&gt;
&lt;p&gt;The new CSS Relative Color Syntax &lt;a href=&quot;https://caniuse.com/css-relative-colors&quot;&gt;part of baseline 2024&lt;/a&gt; allows you to get the individual channels of a color function and mutate the output. Here’s a simple example:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;background: rgb(from var(--color) r r r);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can identify relative color with the &lt;code&gt;from&lt;/code&gt; keyword in the color function. What this would do is take the red channel of the &lt;code&gt;var(--color)&lt;/code&gt; and assign that value to the R, G, and B channels of a new color. In other words, using the red channel of the given color to make a new grayscale color. You should see that this is the missing piece to Josh’s idea, so here’s how we’re going to put it together.&lt;/p&gt;
&lt;p&gt;First, we’re going to simplify Josh’s implementation a bit. Here’s the calculation we’re going to use instead:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;clamp(0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (((r &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .299) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;g&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .587) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .114)) - 128) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -1000&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 255);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing it this way omits the &lt;code&gt;/ 1000&lt;/code&gt; by using decimals for the magic luminance numbers. This will return the amount for each RGB channel to be either black or white. This means we’d need to duplicate the same calculation a few times in the relative color function.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;rgb(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    from var(--color)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    clamp(0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (((r &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .299) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;g&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .587) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .114)) - 128) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -1000&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 255)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    clamp(0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (((r &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .299) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;g&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .587) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .114)) - 128) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -1000&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 255)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    clamp(0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (((r &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .299) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;g&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .587) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .114)) - 128) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -1000&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 255)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to duplicate this because the relative color syntax must return a color, not an individual number, so we need to do the same calculation at each channel. It’s sort of like doing the procedure all in a single function but as a requirement due to how relative colors work. This makes the code a little messy, so instead I’ll recommend registering a CSS custom property that can be reused:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --channel {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    syntax: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;    initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: clamp(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        (((r &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .299) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;g&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .587) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; .114)) - 128) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; -1000&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        255&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have that, we can finally use the &lt;code&gt;--channel&lt;/code&gt; variable within the relative color syntax to create the new color:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; #bada55&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    background&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--color)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; rgb&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        from &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--color)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could also opt to hide the relative syntax in another variable so your peers don’t need to remember the syntax and can use a custom property instead:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/* tokens */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; #bada55&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --contrast-color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; rgb&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        from &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--color)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--channel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/* app */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    background&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--color)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--contrast-color)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the codepen below which includes a color picker and notice how the text changes color from black to white depending on your chosen color:&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;CSS only WCAG contrast color&quot; src=&quot;https://codepen.io/fauxserious/embed/ZYGBgYQ?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/ZYGBgYQ&quot;&gt;
CSS only WCAG contrast color&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;strong&gt;EDIT (2025-06-04):&lt;/strong&gt; After sharing the approach &lt;a href=&quot;https://matthewstrom.com/&quot;&gt;Matt Ström-Awn&lt;/a&gt; &lt;a href=&quot;https://codepen.io/ilikescience/pen/azOpOqX&quot;&gt;made some improvements&lt;/a&gt;. Here’s a quote from the thread in the Design System’s Slack:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;super cool! you sparked my curiosity; i know that the wcag contrast algorithm uses xyz’s ‘y’ value as its definition of relative luminosity; so if you do the relative color in xyz instead of rgb it simplifies the math a bit.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;color: color(from var(--color) xyz&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; round(up&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; min(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; max(0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; - y)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; round(up&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; min(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; max(0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; - y)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; round(up&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; min(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; max(0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; - y)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Admittedly, I’m not good with color math but &lt;a href=&quot;https://matthewstrom.com/writing/generating-color-palettes/&quot;&gt;I know Matt is&lt;/a&gt; so I trust this is a better approach.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category><category>color</category><category>accessibility</category></item><item><title>Beer goggles</title><link>https://blog.damato.design/posts/beer-goggles</link><guid isPermaLink="true">https://blog.damato.design/posts/beer-goggles</guid><description>You too could be a bored intern and influence a generation of design</description><pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Material Design 3 (Material You) was &lt;a href=&quot;https://m3.material.io/blog/announcing-material-you&quot;&gt;officially unveiled&lt;/a&gt; at Google I/O in May of 2021. At the time the new M3 site was still in the works with only a few analogs to the &lt;a href=&quot;https://m2.material.io/&quot;&gt;earlier Material Design site&lt;/a&gt;. I didn’t think much of this at the time, especially since 2021 was a busy year for me personally. But now, Material has a “new” launch called Expressive with &lt;a href=&quot;https://design.google/library/expressive-material-design-google-research&quot;&gt;an article explaining how we got here&lt;/a&gt;. The article triggers me for a few reasons:&lt;/p&gt;
&lt;h2 id=&quot;boredom&quot;&gt;Boredom&lt;/h2&gt;
&lt;p&gt;One of the first paragraphs in the article has the following quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Back in 2022, our research intern was studying user sentiment toward Material Design in Google apps. After mentioning her initial findings to colleagues in a Munich beer hall, she sparked a team-wide design debate: Why did all these apps look so similar? So boring? Wasn’t there room to dial up the feeling?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One interpretation of this story is that you too can make a difference in a large organization with a small idea. Sounds very inspiring. However, when I read this, it looks like a group of designers were bored. There’s no user need spearheading the work. And this is something we see all the time. Some new designer comes in, looks at the current body of work for something they can own, and turns it into some project for a promotion. This occurs even if everything is working fine.&lt;/p&gt;
&lt;p&gt;Don’t get me wrong, there’s always things that can be improved, but it’s rarely the way that things look. It’s more often the way things work that need improvement. Unfortunately, it’s easier to put a new paint job on some walls than it is to teardown and rebuild parts of the house. A new coat of paint can really help make the room look new, but someone should really consider moving the toilet out of the kitchen.&lt;/p&gt;
&lt;h2 id=&quot;looksgood&quot;&gt;LooksGood™&lt;/h2&gt;
&lt;p&gt;Later in the article we get some “data”:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;These factors can be quantified in users’ responses to new M3 Expressive designs. We found a 32% increase in subculture perception, which indicates that expressive design makes a brand feel more relevant and “in-the-know.” We also saw a 34% boost in modernity, making a brand feel fresh and forward-thinking. On top of that, there was a 30% jump in rebelliousness, suggesting that expressive design positions a brand as bold, innovative, and willing to break from convention.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These numbers are points describing the &lt;a href=&quot;https://lawsofux.com/aesthetic-usability-effect/&quot;&gt;Aesthetic-Usability Effect&lt;/a&gt;. Imagine conducting a survey and asking people about their feelings toward cotton candy versus a shovel. You’re going to get an overwhelming amount of positive responses for things that look fun and inviting over things that are designed to solve a user problem. If you then ask about the feelings between a pastel shovel and a brown shovel, you’ll probably get more people liking the pastel one because it looks unique and fun.&lt;/p&gt;
&lt;p&gt;The bottom line here is that we need things to be usable &lt;em&gt;first&lt;/em&gt;. We shouldn’t be triggering a redesign unless we need a hole and all we have is cotton candy.&lt;/p&gt;
&lt;h2 id=&quot;false-positives&quot;&gt;False positives&lt;/h2&gt;
&lt;p&gt;The next is an image from the article:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/expressive-email.png&quot; alt=&quot;Side-by-side of email application with design changes&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The article claims that the participants were able to find the send button 4 times faster with the Expressive redesign specifically because of the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By making the Send button larger and more prominent…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, I’d argue that the size and cosmetic treatment didn’t have the largest impact here. It certainly contributes, but the &lt;em&gt;placement&lt;/em&gt; change is more of a signficant factor. To demonstrate, consider if we were to imagine eye-tracking on writing an email. Your eyes should be following the words as you type, left-to-right moving downward. This means when you are finished, your eyes will be lower on the screen.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/expressive-email-eyes.png&quot; alt=&quot;Side-by-side of email application with design changes and eye path&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This also means that the send button in the original application is in a suboptimal spot at the top-right corner. This isn’t just due to how we read, but items on the top edge are often overlooked in user interfaces as they are normally reserved for more generic items like user accounts or settings. The main action should be more prominently featured within the context of the user task.&lt;/p&gt;
&lt;p&gt;This is the difficulty with testing too many changes at once. You may wind up with a more usable solution but not know what of the changes was the definitive improvement. Changing the size, color, and placement of the button all at once can make it seem like the cosmetic changes were the qualities that provided the impact and falsy drive more resources into something that didn’t truly need it. Imagine painting your bedroom and realizing you have a lot of left over paint. So you decide to repaint all of the walls. They didn’t need to be repainted, you just did it because you were already motivated for that task. Meanwhile, the toilet is still in the kitchen.&lt;/p&gt;
&lt;h2 id=&quot;unfamiliarity&quot;&gt;Unfamiliarity&lt;/h2&gt;
&lt;p&gt;One final quote at the bottom of the article:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is also worth noting that while expressive design improved preference and usability, it was impacted by users’ lack of familiarity. This is a challenge that designers will have to navigate, but we expect to see familiarity increase as more and more apps adopt this new style in the coming months.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This paragraph is justifying what happened during the hamburger menu explosion of 2016 and beyond. There’s plenty of articles showing both sides of the story across the internet &lt;a href=&quot;https://adactio.com/journal/10120&quot;&gt;ending up in a firm It Depends™&lt;/a&gt; from the community. When there’s so much debate, it’s easy to get lost in the opinions. The reality is that you want familiarity. This is &lt;a href=&quot;https://lawsofux.com/jakobs-law/&quot;&gt;Jakob’s Law&lt;/a&gt;. The more you make things unique and unfamiliar, the harder it will be for people to use.&lt;/p&gt;
&lt;p&gt;Take the new expressive email interface from before. I can’t tell you what all the other icon buttons next to the send button do. Why is it “image” and then “camera”? Can’t I see my images from the camera? Maybe the next one is a document scanner? Does the scanner write the document into my email or does it just attach like the paperclip used to do? I have no clue what the fourth icon is supposed to be. That last icon looks like a pen so maybe I can draw something in the email? All mostly unclear and unfamiliar.&lt;/p&gt;
&lt;h2 id=&quot;take-caution&quot;&gt;Take caution&lt;/h2&gt;
&lt;p&gt;The main point I’m making here is we’re seeing more designers going for low-hanging fruit of cosmetic changes instead of truly improving our experiences. I’m afraid that design teams are going to look at this “research” to justify their own exploration into expressiveness while neglecting better experiences for users. A technique like &lt;a href=&quot;https://mode.place/&quot;&gt;Mise en Mode&lt;/a&gt; can provide infinite amounts of expression. But at the end of the day, you still haven’t moved that toilet from the kitchen.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>design</category></item><item><title>Done with components</title><link>https://blog.damato.design/posts/done-with-components</link><guid isPermaLink="true">https://blog.damato.design/posts/done-with-components</guid><description>Component libraries aren&apos;t design systems until people start caring.</description><pubDate>Wed, 30 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m done building components. 🙅&lt;/p&gt;
&lt;p&gt;This feeling was already strong for me. Teams are always in need of their own components for experiences that are unique to the platform. That means they need their own infrastructure for component development. These get built in isolation and don’t have the same level of communication that components in the core design system would have. This is something that a design system can’t stop and really shouldn’t stop.&lt;/p&gt;
&lt;p&gt;That’s why I see a future in the “component marketplace”, where all components are meant to be built which gives visibility not only across teams who want to use existing resources other teams have built but for leadership to clearly understand the landscape of their design language. Its consistency or its lack thereof. This would require some sort of desire for teams to start building on the marketplace. Maybe it gives them auto-docs or a testing harness that is easy to use. Maybe it gives them metrics into how people are using their stuff. Maybe there’s some other attraction that I’m overlooking.&lt;/p&gt;
&lt;p&gt;I know what you’re thinking, this is how we get a dozen different buttons across the organization because anyone can put them there. And that’s the point. If the organization wants all of those buttons, they can have them. And that’s often how a design system starts. We do the classic “button audit” find that there’s 12 different kinds and the design system team is born. But what if that audit was not a moment in time, but a dashboard? What if your design leadership could open it up at any time and decide if this was acceptable?&lt;/p&gt;
&lt;p&gt;I think this visibility and access would clearly describe the behavior of design leadership. Organizations that want consistency would take actionable steps against the resources found in the marketplace, potentially sending out their design systems team to work with those creators for alignment. On the flip side, organizations that don’t care will continue not caring and it’ll show very clearly. Until then, design leadership can blissfully hide behind not having enough resources to do the audit and no one can be sure there are inconsistencies at all. 🫣&lt;/p&gt;
&lt;h2 id=&quot;the-last-straw&quot;&gt;The last straw&lt;/h2&gt;
&lt;p&gt;This past weekend, my disdain for building components was solidified. I realized what a definitive difference is between a design system and a component library was.&lt;/p&gt;
&lt;p&gt;I’ve always said that a design system is a matter of agreement. It’s what the organization collectively agrees should be the overall strategy when it comes to design. However, what happens when that agreement is &lt;em&gt;objectively&lt;/em&gt; wrong? This can be compounded by larger organizations proudly displaying poor choices. Those choices are replicated and then defended by the bandwagon effect. This results in a component library. They’re just components based on feelings. People want components, so they get components.&lt;/p&gt;
&lt;p&gt;Does a design system have a component library? Sure, but the difference is the thoughtfulness of their inclusion. Is it easy to make your own button? Sure, but it probably doesn’t account for &lt;a href=&quot;https://react-spectrum.adobe.com/blog/building-a-button-part-1.html&quot;&gt;the 3-part blog post published by Adobe Spectrum about handling touch devices due to various browser inconsistencies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But who cares right? The deadline is tomorrow, and you just need a button that LooksGood™. You have no time to understand all that goes into it. You just see that it isn’t exactly the button that you need so you make your own. How hard could it be? It works on my machine and everyone uses a Mac 16” Pro Ultimate HD Max (I have no idea what the names of these things are). Button #13 is born.&lt;/p&gt;
&lt;p&gt;That’s why I’m now making a clear distinction between a component library and a design system. The collection of various shiny objects and the thoughtful curation of related usable resources.&lt;/p&gt;
&lt;p&gt;Importantly, just because thought was involved doesn’t immediately make it a design system. You could &lt;em&gt;think&lt;/em&gt; that a certain use-case is an exception, but oftentimes that’s where the thinking stops. The design system is meant to have the resources to take all of the information and synthesize it down to the perfect component. Realistically, we don’t have resources to do this. Either we don’t get the time, cooperation, or complete requirements. It’s garbage in and garbage out. No wonder people are losing faith in design systems.&lt;/p&gt;
&lt;p&gt;But I haven’t. That’s why I’ve dedicated my free time to them because I’m confident there are perfect versions of them. Versions that could work for everyone. And continuing to copy existing systems strapped for resources themselves isn’t where we’ll find improvements. We need to pivot for &lt;a href=&quot;https://bradfrost.com/blog/post/a-global-design-system/&quot;&gt;universal solutions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is because I’m a user too. And every time I visit a site with wild inconsistencies and poor usability I know I have the experience to make an impact and you can too. 💪&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>components</category></item><item><title>Out of order</title><link>https://blog.damato.design/posts/out-of-order</link><guid isPermaLink="true">https://blog.damato.design/posts/out-of-order</guid><description>CSS does a lot these days, and I argue that it does too much.</description><pubDate>Wed, 16 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As someone who like to think he deeply understands the CSS I’m writing, I do everything I can to avoid using CSS that can cause unintended side effects. As an example, for my new &lt;a href=&quot;https://donnie.damato.design&quot;&gt;personal website&lt;/a&gt;, I use &lt;code&gt;z-index&lt;/code&gt; &lt;em&gt;once&lt;/em&gt; to handle the page tearing animation. Trying to manipulate the order of things using CSS can seem reasonable at first but often has hidden problems that you might not notice. I’d like to outline some of those problems here.&lt;/p&gt;
&lt;h2 id=&quot;whos-on-first&quot;&gt;Who’s on first?&lt;/h2&gt;
&lt;p&gt;Let’s say we have a dropdown component which produces some sort of flyout experience. We believe that setting some high &lt;code&gt;z-index&lt;/code&gt; value will ensure that it is always on top of the content:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.dropdown&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    z-index&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 100&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, when we create the modal component, we set it as a higher &lt;code&gt;z-index&lt;/code&gt; because it is meant to be disruptive and on top of everything:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.modal&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    z-index&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The question here is, what happens when the dropdown component is meant to be part of the modal composition? For example, perhaps you need to select from a large list of options within the modal. In the current setup, the flyout could appear &lt;em&gt;underneath&lt;/em&gt; the modal because the &lt;code&gt;z-index&lt;/code&gt; value for the dropdown is lower than the modal.&lt;/p&gt;
&lt;p&gt;Now, when many folks find this problem, they start adding exceptions. Ok when &lt;em&gt;this specific dropdown&lt;/em&gt; appears, we’re going to increase the &lt;code&gt;z-index&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.dropdown.but-only-this-one&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    z-index&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 300&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sure, this could work. But then when large teams are trying to use these components it turns into a priority war. With more things that are meant to appear on top of each other, it becomes less clear which items are more important at which times. Everyone thinks their use is the exception to the rule and then no system truly exists.&lt;/p&gt;
&lt;p&gt;Some design systems took &lt;a href=&quot;https://m2.material.io/design/environment/elevation.html#default-elevations&quot;&gt;Material’s guidelines for elevation&lt;/a&gt; and turned them into design tokens for &lt;code&gt;z-index&lt;/code&gt;. Unfortunately, based on my example above, this doesn’t work. It’s possible that your tooltip is meant to appear in a modal, causing its &lt;code&gt;z-index&lt;/code&gt; number to increase.&lt;/p&gt;
&lt;p&gt;So what’s the solution? Avoid using &lt;code&gt;z-index&lt;/code&gt; altogether.&lt;/p&gt;
&lt;p&gt;When you start working with the &lt;code&gt;position&lt;/code&gt; property, and then try reordering the elements, you should notice a certain quality. Having nothing else set, the order that the elements appear matches the DOM order. In other words, later elements will appear on top of elements that come before. In the following example, try rearranging the HTML elements without touching the CSS.&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Positioning&quot; src=&quot;https://codepen.io/fauxserious/embed/vEEYJoe?default-tab=html%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/vEEYJoe&quot;&gt;
Positioning&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;Notice that whatever box is rendered last, is the box that appears on top of the others. This means if we want things to appear on top of other things, we should be rendering them last in the DOM order.&lt;/p&gt;
&lt;p&gt;In fact, this is exactly how the new &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Top_layer&quot;&gt;top-layer&lt;/a&gt; works in the DOM. The top-layer is exactly what it sounds like, it is a layer that exists on top of everything else in the page. That means even trying to set &lt;code&gt;z-index: Infinity&lt;/code&gt; will not work here. Speaking of &lt;code&gt;z-index&lt;/code&gt;, that doesn’t work on elements in this layer either. The only way to affect the order of elements here is by DOM order. The last rendered element in the layer is the element that appears on top. If you want something to be on top of that, you’ll need to append it to the layer.&lt;/p&gt;
&lt;h2 id=&quot;inset-fill&quot;&gt;Inset fill&lt;/h2&gt;
&lt;p&gt;But, let’s say you don’t need to do any flyouts or disruptions (good!). You want to do something more simple like have an element fill a container without itself contributing to the size of the container. Traditionally, we’d use the inset fill technique to do this.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.parent&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    position&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; relative&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.child&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    position&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; absolute&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    inset&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/* same as setting top, left, bottom, right all to 0 */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was very common when we needed maintain the aspect ratio for media by using the &lt;a href=&quot;https://css-tricks.com/aspect-ratio-boxes/&quot;&gt;padding hack&lt;/a&gt;. While we don’t need that specific hack any longer, there’s still use-cases for making an element fill a parent container. In those cases, I still recommend avoiding the use of &lt;code&gt;z-index&lt;/code&gt; by making the element to appear on top last in the DOM order. But I’d also recommend avoiding the &lt;code&gt;position&lt;/code&gt; property altogether.&lt;/p&gt;
&lt;p&gt;Josh Comeau has &lt;a href=&quot;https://www.joshwcomeau.com/css/stacking-contexts/&quot;&gt;a great post about stacking contexts&lt;/a&gt; that you should absolutely read. But the tl;dr is that using &lt;code&gt;position&lt;/code&gt; creates a stacking context. This is good for when you want to use &lt;code&gt;z-index&lt;/code&gt; but bad when other stacking contexts are created elsewhere and conflict in some way. Most people don’t know &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Stacking_context#features_creating_stacking_contexts&quot;&gt;the properties that create stacking contexts&lt;/a&gt;, but these are usually the culprits to when you think you need to start introducing &lt;code&gt;z-index&lt;/code&gt; into your CSS. Here’s a few important points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you create a container (for container queries) with &lt;code&gt;inline-size&lt;/code&gt; or &lt;code&gt;size&lt;/code&gt;, that creates a new stacking context.&lt;/li&gt;
&lt;li&gt;If you set &lt;code&gt;opacity&lt;/code&gt; on something to anything less than 1, that creates a new stacking context.&lt;/li&gt;
&lt;li&gt;Nearly anything related to transforms, filters, masks, and blend modes will create a new stacking context.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This illustrates one of the things that makes people upset about CSS. There are properties that can affect other things in the composition in ways you don’t expect. You’d think that simply setting &lt;code&gt;opacity: .5;&lt;/code&gt; is just making the element translucent, but there’s other side effects associated with it that could be unintended. Unfortunately, the only way to really know these is with experience. The more you avoid learning this, the more you’ll wonder what is happening every moment you begrudgingly write CSS.&lt;/p&gt;
&lt;p&gt;Getting back to the inset fill technique. Instead of using &lt;code&gt;position&lt;/code&gt;, I recommend trying to use &lt;code&gt;display: grid;&lt;/code&gt; instead:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.parent&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    display&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; grid&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** or inline-grid */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.child&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    grid-area&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1 / 1 / -1 / -1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;grid-area&lt;/code&gt; set in the child says to make sure this item touches all of the edges of the parent grid, filling the space. This forces the element to appear on top of any other children that may be within the grid.&lt;/p&gt;
&lt;h2 id=&quot;reading-flow&quot;&gt;Reading flow&lt;/h2&gt;
&lt;p&gt;Ordering things in the third dimension isn’t the only way we can affect elements using CSS. We’ve had the &lt;code&gt;order&lt;/code&gt; property since the introduction of Flexbox. This has the ability to visually rearrange the elements in a composition without changing their order in the DOM.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.flex-child&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    order&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s other properties that can change the visual order, such as &lt;code&gt;flex-direction&lt;/code&gt; and even &lt;code&gt;grid-areas&lt;/code&gt;. However, my recommendation is to avoid doing this, and if you must change the order to update the DOM order instead.&lt;/p&gt;
&lt;p&gt;Now I know what you’re probably thinking, using JavaScript to rearrange the DOM sounds expensive when CSS would be much more performant and that’s true. The problem comes down to accessibility. The page structure should match the visual order of elements. If it doesn’t, then folks who can’t see the visual rearrangement can be confused about the order of the elements.&lt;/p&gt;
&lt;p&gt;Imagine that someone who uses an assistive technology has called a help line for assistance with their account. The person on the phone says, “click the first button”. If the DOM order doesn’t match the visual order, the first button could be different to these folks and cause confusion down the line.&lt;/p&gt;
&lt;p&gt;I keep this in mind when designing, and consider the ways I could keep the order maintained when the layout needs to change based on the available size. Sometimes though, you get a design in your inbox that can’t have reordering avoided. That’s when as of right now I’d recommend using a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver&quot;&gt;&lt;code&gt;ResizeObserver&lt;/code&gt;&lt;/a&gt; to reflow the elements instead of CSS for the reasons I’ve described above. &lt;a href=&quot;https://github.com/damato-design/system/blob/main/src/components/Box/reflow.ts&quot;&gt;Here’s an implementation&lt;/a&gt; for my design system playground.&lt;/p&gt;
&lt;h2 id=&quot;bring-order&quot;&gt;Bring order&lt;/h2&gt;
&lt;p&gt;Good things are on the way! There’s a new CSS property being discussed called &lt;code&gt;reading-flow&lt;/code&gt; which is designed to help the element order problems I’ve described above. If this property becomes available, then we could tell the browser what order the elements should be in if the visual order doesn’t match. Rachel Andrew has &lt;a href=&quot;https://chrome.dev/reading-flow-examples/&quot;&gt;some examples&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/watch?v=X6azWrtHS-k&quot;&gt;a presentation&lt;/a&gt; on this. This is relating directly to the &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/11243&quot;&gt;Masonry discussions&lt;/a&gt; to help understand what the intended flow is meant to be for the user. Having this along with top-layer will make setting the order of elements more clear than ever before.&lt;/p&gt;
&lt;p&gt;Bottom line is that when you are trying to update the order of elements using CSS, pause to think of the side-effects of these decisions and refer to some of the recommendations here to help keep a consistent structure.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>accessibility</category><category>css</category><category>design</category></item><item><title>Less Gridless</title><link>https://blog.damato.design/posts/less-gridless</link><guid isPermaLink="true">https://blog.damato.design/posts/less-gridless</guid><description>Maybe the industry has finally figured it out.</description><pubDate>Wed, 09 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I released &lt;a href=&quot;https://gridless.design&quot;&gt;Gridless Design&lt;/a&gt; nearly 4 years ago as a response to a frustration I saw in the industry. Similar to the desire for responsive design, it was a call for folks making digital applications to take the foundations on the web into consideration when creating things on top of it. It has the same foundational argument attached to “should designers code” because it asks designers to understand that the web is not an artboard. It also helps engineers support designs when a grid is no longer part of the handoff.&lt;/p&gt;
&lt;p&gt;Gridless Design was my first project that got a lot of discussion across the industry. Most leaders in our practice agreed with the take, while others with less experience were &lt;a href=&quot;/posts/feeding-trolls&quot;&gt;skeptical to say the least&lt;/a&gt;. I’ve had analytics tied to the site for a long time, and it continues to be a top performer among my other work. Some of the places that currently link to the work are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://utopia.fyi/&quot;&gt;Utopia.fyi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-irl.info/&quot;&gt;CSS IRL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/&quot;&gt;CSS-Tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bradfrost.com/&quot;&gt;Brad Frost&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://moderncss.dev/&quot;&gt;Modern CSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is among other internal resource sites like Sharepoint and Jira. I’m glad the concept has gotten so popular. And that’s a bit about what this post is about. I think it’s time to sunset the site.&lt;/p&gt;
&lt;h2 id=&quot;grids-are-gone&quot;&gt;Grids are gone&lt;/h2&gt;
&lt;p&gt;4 years later, I can’t remember the last time I saw a design grid being shared amongst designers. Either externally on social media, or internally in product design teams. It would seem that the concept has either been very successful or other factors like responsivie design itself have made it more clear than Gridless Design as a goal could have done alone. Maybe the popularity of Tailwind which doesn’t use a design grid was another facet. Even AI could have been a factor.&lt;/p&gt;
&lt;p&gt;No matter the reason why the design grid has faded into the distance for digital design, that was the purpose for the site. It would seem it has fullfilled its purpose and now I can consider bringing it down.&lt;/p&gt;
&lt;p&gt;While I’ve been writing the book about &lt;a href=&quot;https://mode.place&quot;&gt;Mise en Mode&lt;/a&gt;, I’ve also considered what other topics would be good for a book. When I thought about Gridless Design being my first large work as a possibility, I considered how relevant it would be. This caused all of the reflection I’ve had in this post, which is leading me to this decision. Plus, it always helps to drop a domain that costs $50 per year from your account.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;
&lt;p&gt;Gridless Design isn’t going away completely. It’s clear that the resource has been helpful to some people over the past few years and it could continue to be helpful for folks in years to come. So, here’s the plan:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Finish the book.&lt;/li&gt;
&lt;li&gt;Move &amp;amp; update the content to be hosted at one of my other main properties.&lt;/li&gt;
&lt;li&gt;Update the site explaining the move, include the new link.&lt;/li&gt;
&lt;li&gt;Let the site expire within the year after the site is updated.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Nothing is changing right now. The book is still the priority. Look out for the plan to take effect within the next year.&lt;/p&gt;
&lt;p&gt;It’s been fun getting rid of grid with you.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>community</category></item><item><title>Logical Optical</title><link>https://blog.damato.design/posts/logical-optical</link><guid isPermaLink="true">https://blog.damato.design/posts/logical-optical</guid><description>You don&apos;t have to nudge, you can measure instead.</description><pubDate>Thu, 27 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I got into an argument on LinkedIn this week about design choices. Very on brand for me. It started with this image:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/questionable-alignment.jpg&quot; alt=&quot;Google logo isn&apos;t circular and pixel offsets for browser search bar&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The post was defending the choices made in the image, and went on to say that people who aren’t designers have no business questioning these decisions. The weird part about it was that this person wasn’t a designer according to their profile. Since they’ve blocked me from rebutting I can’t link to the post, but I want to take the opportunity to explain that I understand why the logo is not truly a circle, but I’m less convinced about the bar in the field.&lt;/p&gt;
&lt;h2 id=&quot;the-fallacy-of-optical-alignment&quot;&gt;The fallacy of optical alignment&lt;/h2&gt;
&lt;p&gt;Much of my research is uncovering &lt;em&gt;why&lt;/em&gt; we make design decisions. This makes the decision repeatable by following some set of rules for when a new but understood pattern emerges. When it comes to optical alignment, there’s a large collection of articles made by designers who will explain that some shapes shouldn’t be mathematically centered, and instead should be “nudged” to make them look centered to the eye. I’ll be referencing some of the images from the &lt;a href=&quot;https://medium.com/ringcentral-ux/eyeballing-or-optical-alignment-in-design-4ef5ab2d326f&quot;&gt;very first post at the top of my search results for “optical alignment ui”&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is what we have typically called “optical centering”. And the post above even includes the word “eyeballing”. Here’s an example from the post but is also found in &lt;a href=&quot;https://marvelapp.com/blog/optical-adjustment-logic-vs-designers/&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;https://railsdesigner.com/mathematical-optical-alignment-design/&quot;&gt;similar&lt;/a&gt; &lt;a href=&quot;https://www.linkedin.com/posts/ranausman7330_ui-ux-design-activity-7288418232661659648-6APb/&quot;&gt;articles&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/playbutton-offset.webp&quot; alt=&quot;Play button offset from mathematical center&quot;/&gt;&lt;/p&gt;
&lt;p&gt;When we measure the sides of the icon in the middle, they don’t match. However, the icon looks like its centered in the middle of the container. Is this just how our eyes work? Are our eyes imperfect? No, you are still centering the icon. It’s just using a different center. In fact, the triangle is a great example of &lt;a href=&quot;https://www.youtube.com/watch?v=v_oZ9Pe0yRg&quot;&gt;a shape that has lots of centers&lt;/a&gt;. Here’s an image from &lt;a href=&quot;https://medium.com/@wudaoooooo/the-play-button-is-not-optical-alignment-4cea11bda175&quot;&gt;another post&lt;/a&gt;, which demonstrates how this would work in a circular container:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/playbutton-incircle.webp&quot; alt=&quot;Play button offset from mathematical center&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The post where that image is from also shows that the technique being described doesn’t necessarily work for all icons. All icons are not squares or triangles so how are we suppose to think of this? Let’s look at another example which is linked to optical alignment.&lt;/p&gt;
&lt;h2 id=&quot;area-not-dimension&quot;&gt;Area not dimension&lt;/h2&gt;
&lt;p&gt;Here’s another image from the first article:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/square-circle.webp&quot; alt=&quot;A square and a cirlce with the same height&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The post explains that we need to increase the size of the circle because the square looks larger. However, the post doesn’t explain how much you are meant to increase it by. Is it until it LooksGood™? Even &lt;a href=&quot;https://medium.com/design-bridges/optical-effects-9fca82b4cd9a&quot;&gt;the post they’ve linked to&lt;/a&gt; doesn’t explain how to do it. The best suggestion is to blur the icon but that seems even less precise. So what’s the answer?&lt;/p&gt;
&lt;p&gt;The visual weight looks different between the square and circle of the same height because they &lt;em&gt;are&lt;/em&gt; different. What we actually want is shapes of a similar area. Here’s an image from &lt;a href=&quot;https://blog.kleinpixelagency.com/perfect-your-designs-with-imperfection-guide-to-optical-alignment-f76999b5d62d&quot;&gt;another post&lt;/a&gt; that discusses optical alignment:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/equal-area-shapes.webp&quot; alt=&quot;Square and cirlce of equal area showing visual balance&quot;/&gt;&lt;/p&gt;
&lt;p&gt;As you can see, we now have a repeatable process to help understand &lt;em&gt;why&lt;/em&gt; the circle is larger. In fact, we have created tools to help us with this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/keyline-grid.jpg&quot; alt=&quot;Material Design Keyline Grid&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This is the keyline grid provided from &lt;a href=&quot;https://m2.material.io/design/iconography/system-icons.html#grid-and-keyline-shapes&quot;&gt;Google’s Material Design documentation&lt;/a&gt;. That’s right, the guy who wrote &lt;a href=&quot;https://gridless.design&quot;&gt;Gridless Design&lt;/a&gt; is recommending a grid as a tool. This is used to help designers draw icons with a consistent size and alignment. There’s more detail about how to use this grid at the documentation page.&lt;/p&gt;
&lt;p&gt;But let’s look deeper into this grid with our new found knowledge. Let’s measure the area of the main shapes provided by the grid. If we count the number of grid squares we should get an approximation for the area of the circle found within the keylines using the following formula for a circle:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;msup&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;100&lt;/mn&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;314&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi r^2= \pi 100 \approx 314&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;314&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So 314 grid units is our approximation for the area of the circle. Now let’s check the area of the square found within the keylines:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;18&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;324&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;w^2= 18^2= 324&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02691em;&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;324&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Is this the best we can do for the grid? Let’s check where the width is a smaller or larger grid:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;256&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;w^2= 16^2= 256&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02691em;&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;256&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;400&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;w^2= 20^2= 400&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02691em;&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;400&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can see that this is the closest we can get to have the circle and square match in area. Only off by about 10 grid units. With a higher fidelity grid, we could get even more precise. But the bottom line here is that we’re trying to make shapes of equal area when we are “optically” sizing these elements. That’s why this grid is recommended and why it works.&lt;/p&gt;
&lt;p&gt;Math isn’t being thrown out the window in favor of design, most people are using the wrong math.&lt;/p&gt;
&lt;h2 id=&quot;circling-back&quot;&gt;Circling back&lt;/h2&gt;
&lt;p&gt;Returning to the image that started it all. I’m not a typographer, so I’m under qualified to speak about how to adjust the G in the logo so it LooksGood™. And in reply to the post, I said that right now I’d chalk it up to pure brand personality. I have confidence there are qualities of typographic glyphs that follow some of the thinking I’ve demonstrated here but I don’t know precisely how they relate so I won’t comment on it now.&lt;/p&gt;
&lt;p&gt;However, that bar in the search field. I can’t agree with that. And it’s true I’ve never noticed it before, and often times when I’m just navigating around a user-interface I don’t notice things that are off by a few pixels. This isn’t because I’m not a designer (though I do consider myself one), this is because I’m not spending the amount of time staring at a user interface decision to care about what went into it. Only unless it looks out of place while I’m using it does it catch my eye and these few pixels weren’t enough to stop me.&lt;/p&gt;
&lt;p&gt;This describes one of the largest biases in our work, staring at it for too long. In the best case scenario, the user doesn’t remember &lt;em&gt;anything&lt;/em&gt; about your work. They got the task done, and they moved on with life. The amount of time some of industry spends staring at the details can be wasteful in the grand opera of user experience. So if the space between top and bottom of this bar was deliberate, I’m not confident many people would have noticed unless you had the time to stare at it.&lt;/p&gt;
&lt;p&gt;But since we’re looking at it, let’s talk about it. Or rather, let’s talk about another search bar that the original author shared in the comments: &lt;a href=&quot;https://atlassian.design&quot;&gt;Atlassian’s search bar&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/atlassian-search.jpeg&quot; alt=&quot;Atlassian search bar&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The original poster argued that if Atlassian is also misaligning their icons that it all must be some conspiricy against proper alignment techniques. That response is an exaggeration instead of taking a moment to think &lt;em&gt;why&lt;/em&gt; they could have done this. Let’s look at a few things by making some lines and pixel measurements.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/atlassian-search-lines.png&quot; alt=&quot;Atlassian search bar with grid lines&quot;/&gt;&lt;/p&gt;
&lt;p&gt;One of the first things we can see is that there &lt;em&gt;is&lt;/em&gt; alignment. The team may have chose to align the icon to the baseline of the text. The next thing we can see is that it would seem that the text isn’t truly centered within the frame. Is that intentional? No, at least not in the way the web designers were expecting it to happen. We can see what’s happening when we inspect the box in devtools.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/atlassian-search-boxmodel.png&quot; alt=&quot;Atlassian search bar box model&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Measuring by eye in my editor isn’t getting me perfect results! What gives? This is most likely due to how the font sits within the line-height box. There’s &lt;a href=&quot;https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align&quot;&gt;a great post about this&lt;/a&gt; if you want to dive into it. But what’s most likely happening is that the font doesn’t sit in the vertical center of the &lt;code&gt;24px&lt;/code&gt; line height box, which causes the space on top to be larger than the bottom. If this was a custom font provided by Atlassian, they would be in control of how the font sits in this box. However, they’ve chosen to use &lt;code&gt;ui-sans-serif&lt;/code&gt; which will use the current font of the operating system the user is using. So they have no real control about which font that is or how that font sits in the line height box.&lt;/p&gt;
&lt;p&gt;Back to the icon, if we continue inspecting, we’ll find that the icon is &lt;em&gt;not&lt;/em&gt; found within a perfect square even though the icon width and height are declared identically.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/atlassian-search-iconsize.png&quot; alt=&quot;Atlassian search bar icon sizing&quot;/&gt;&lt;/p&gt;
&lt;p&gt;How could this be intentional if the size is declared as &lt;code&gt;24&lt;/code&gt; but not rendering as 24? In my opinion, this wasn’t intentional. The fact is that the amount of difference between the rendering and the intented size is so slight that it wouldn’t matter to look into it further. The icon LooksGood™ so we ship it. In this case, it’s doing the job so it’s fine to move on to something else.&lt;/p&gt;
&lt;p&gt;However, if they really wanted to make the icon a 24x24 square, it’s only a matter of adding &lt;code&gt;flex-shrink: 0&lt;/code&gt; to the direct child of the flex container holding the icon and the input elements. This is a common rule &lt;a href=&quot;https://github.com/damato-design/system/blob/main/src/components/Icon/styles.module.scss#L17&quot;&gt;I tend to put on my icon elements&lt;/a&gt; when they appear in flex containers because they can be easily squished. The &lt;code&gt;flex-shrink: 0&lt;/code&gt; tells the &lt;code&gt;&amp;lt;svg/&amp;gt;&lt;/code&gt; that it shouldn’t be affected by siblings that are growing to take up space.&lt;/p&gt;
&lt;p&gt;As for centering to the baseline, this could be intentional or it could just be an artifact of the elements used to compose this component. The element being used to hold the icon is a &lt;code&gt;&amp;lt;span/&amp;gt;&lt;/code&gt; element which is classified as an inline element so it behaves like text. This means its size can be affected by line height among other properties. If we were to change this to &lt;code&gt;display: flex&lt;/code&gt; (or even &lt;code&gt;inline-flex&lt;/code&gt;), then the box doesn’t behave like an inline element anymore because the flex algorithm takes precedence. Making that change, along with the earlier &lt;code&gt;flex-shrink: 0&lt;/code&gt; change on this element inside the current flex container set with &lt;code&gt;align-items: center&lt;/code&gt; would vertically align the icon and text as best as the CSS algorithm can do.&lt;/p&gt;
&lt;p&gt;Whether or not these decisions were deliberate or oversights, I can’t tell from the outside. Only the author of the component could disclose that. We’d always like to think these large companies are making choices intentionally but in reality there’s often so much going on with not enough resources that it’s often a matter of being good enough. &lt;a href=&quot;/posts/close-thy-enemy/&quot;&gt;I’ve found other areas&lt;/a&gt; where improvements could be made but you have to weigh the effort over the impact. Does it really matter if the icon isn’t perfectly centered? Will the user care or will it stop them from proceeding?&lt;/p&gt;
&lt;h2 id=&quot;space-bar&quot;&gt;Space bar&lt;/h2&gt;
&lt;p&gt;So when it comes to the bar in the original image, my best guess there isn’t that it was planned to line up with the icons outside of the search bar, since that is against &lt;a href=&quot;https://lawsofux.com/law-of-proximity/&quot;&gt;Gestalt’s law of proximity&lt;/a&gt;. It’s more likely that it was an unnoticed few pixels in an otherwise good enough to ship component. If they actually do line up with the icons outside, I think that’s more serendipitous than meticulous. Die-hard designers can justify this all they want with “known design principles” but I find understanding &lt;em&gt;why&lt;/em&gt; to be more powerful and harder to refute. Since no one but the original implementer could tell us, all folks tend to do is assume that it was intentional because this is a big company. As we now know this doesn’t need to be the only story.&lt;/p&gt;
&lt;p&gt;The point here is that you shouldn’t just take what other folks have said as gospel, especially in the case of design at large organizations. Challenging the norms is how we get innovation as long as we explain the reasoning behind what we do. When you explain those repeatable reasons, you are more likely to gain agreement than through trying to convey your feelings. The difficulty here is that we cannot itemize every single decision we make in design, especially in the production environments of building products. In many cases, we just need to get the proof of concept out the door and good enough for the next round of funding. That’s why we hear so many cases of poor accessibility, performance, and personalization in the majority of experiences. We have no time to explain why, it needs to be out yesterday. We have no time to fix it when we find out it could be improved, we have another priority. That gets repeated by other smaller organizations hoping that the big dogs did it well and that’s how we get bad patterns, &lt;a href=&quot;https://medium.com/google-design/the-evolution-of-material-designs-text-fields-603688b3fe03&quot;&gt;like floating labels&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And when a design systems architect tells you they have experience in this topic, blocking them from the conversation really emphasizes how much your argument LooksGood™. 😏&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>design</category></item><item><title>Defending Tailwind</title><link>https://blog.damato.design/posts/defending-tailwind</link><guid isPermaLink="true">https://blog.damato.design/posts/defending-tailwind</guid><description>I don&apos;t like it, but I get it.</description><pubDate>Thu, 06 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you told me 2 years ago that I was going to write this post, I would have scoffed at the thought. But after a deeper think related to the current state of affairs when it comes to development today, I get it.&lt;/p&gt;
&lt;p&gt;The thumbnail used to promote the &lt;a href=&quot;https://www.buzzsprout.com/1958356/episodes/14701631-15-donnie-d-amato-what-s-a-component&quot;&gt;Code &amp;amp; Pixels&lt;/a&gt; podcast says it all.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/codepixels-thumbnail.jpg&quot; alt=&quot;Me, with the text &amp;#34;I don&apos;t like Tailwind&amp;#34;&quot;/&gt;&lt;/p&gt;
&lt;p&gt;I’m very confident that there will never be a time where I will consciously choose Tailwind to style my work. I have too much experience working with CSS to need it. But it wasn’t until a few years ago that I had a wake up call about the gap between my skill and the skills of over developers around me.&lt;/p&gt;
&lt;h2 id=&quot;hiring-greatness&quot;&gt;Hiring greatness&lt;/h2&gt;
&lt;p&gt;When I worked for Compass, I was a part of practically every frontend engineering hire starting at my 3rd week of employment there. I’ve attributed it to the person asking me the CSS question disclosing that it took him an entire week to do what I did in 20 minutes during the interview. He had nothing left to ask after blowing away any doubt I’d be able to do the job.&lt;/p&gt;
&lt;p&gt;In that time, we created what I can only describe as the best frontend engineering team I’ll probably ever see. Every person with the title frontend engineer was solid in CSS. No problems centering elements, boxes aligned nicely, and responsive design was at the top of everyone’s mind. I was still involved to kickstart newer projects with complex layout needs. The use of CSS grid on the &lt;a href=&quot;https://www.compass.com/listing/44-prospect-park-west-unit-a1-brooklyn-ny-11215/1774010098596006345/&quot;&gt;listing page&lt;/a&gt; is still largely of my doing, now over 6 years old.&lt;/p&gt;
&lt;p&gt;I got a rude awakening when leaving Compass, when new my senior director told me that I was an outlier. I didn’t know how true that was until more recently.&lt;/p&gt;
&lt;h2 id=&quot;more-important&quot;&gt;More important&lt;/h2&gt;
&lt;p&gt;What I’m realizing now is there’s more of a focus on just getting it done. An engineer gets a design thrown over the fence and their job is to “just do it”. The organization wants to get it done as quickly as possible. This means there’s a lack of care in things that are deprioritized. When the majority of full stack engineers are trying to wrangle CSS, it isn’t important to them how to organize that CSS. They just want to set this color on that element and move on to more important things.&lt;/p&gt;
&lt;p&gt;That’s where Tailwind comes in.&lt;/p&gt;
&lt;h2 id=&quot;whats-good&quot;&gt;What’s “good”&lt;/h2&gt;
&lt;p&gt;There’s two “good” things about Tailwind which I’ll address separately in paragraphs below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You don’t need to think of a name for the thing that needs styles.&lt;/li&gt;
&lt;li&gt;There’s no context switching between the file where you write styles to the file where those styles are being applied.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I can definitely agree that naming something &lt;code&gt;.wrapper&lt;/code&gt; is awful. It gives no context to where it is or what it is doing. It’s often a challenge to think of a good name to provide a hook for these styles. Especially styles that are being used once and in a project that might have multiple teams trying to name their &lt;code&gt;.card&lt;/code&gt; and having things conflict. Going the small step of namespacing the class names manually is too much forethought, so we’d rather complain that CSS is broken.&lt;/p&gt;
&lt;p&gt;Should we assume that a good engineer would have good naming skills? We could, but in most facets of programming you don’t need to because of scope. There’s countless &lt;code&gt;main()&lt;/code&gt; calls out there. The feature of CSS being global causes headaches for folks looking for scoped styles. Overrides bleed into unintended places and it becomes a big hassle in the way of more important work.&lt;/p&gt;
&lt;p&gt;This is a reason why libraries like &lt;a href=&quot;https://styled-components.com/&quot;&gt;Styled Components&lt;/a&gt; got so popular. The component you created in React was created from declaring styles.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; Title&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; styled&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  font-size: 1.5em;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  text-align: center;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  color: #BF4F74;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Boom! You’ve created a component and added the styles in one shot. And because of the hashed classes underneath, conflicts are avoided. I actually agree with this approach. This is a component-first way of thinking. The reuse is in the components, not in the styles. It’s a lot of the reason why I adopted Shadow DOM early on. Tailwind does this too, just with more native syntax in HTML.&lt;/p&gt;
&lt;p&gt;This makes way into the second point of context switching. Historically, you’d have to create something in your HTML structure for you to then hook into with CSS styles in a separate file. You’d have to remember what that name was in order to style it. On top of this, you’d also have to switch to the rendered result to see what the effect was. There’s a lot of screens to switch in between just to see what’s happening. It makes a lot of sense to reduce that by writing everything in a single file.&lt;/p&gt;
&lt;p&gt;I have empathy for this since recently writing Astro components. All the code for an Astro component is in a single &lt;code&gt;.astro&lt;/code&gt; file. I’ve noticed that I tend to write a few lines of HTML, maybe even less of JS, but the large majority of the file is CSS. It’s clear to me that much of the time spent coding in frameworks is CSS and engineers are going to want to reduce that time.&lt;/p&gt;
&lt;p&gt;With Tailwind, a person can write the HTML and CSS in one shot. Is it a lot of HTML now? Most likely, but we can tuck that away in the code editor. The organization of styles is wired to the organization of the HTML. It’s a two-for-one special and that must be better, right?&lt;/p&gt;
&lt;h2 id=&quot;whats-bad&quot;&gt;What’s bad&lt;/h2&gt;
&lt;p&gt;Responsibility and scale.&lt;/p&gt;
&lt;p&gt;The naming convention that Tailwind uses forces the engineer to be in charge of when a CSS value gets applied. If you want to have a theming system that can change between different values, what &lt;code&gt;color-red-500&lt;/code&gt; means can’t change, it must continue to be a red. It’s entirely possible that the &lt;code&gt;color-red-500&lt;/code&gt; is not appropriate for the new context, and some engineer must go in and add new logic with some new class name.&lt;/p&gt;
&lt;p&gt;I don’t know about other folks, but I don’t want my role to be reduced to responding to every call for a new cosmetic change in the interface. Adding new logic to juggle these classes. I want the folks who ask for the change to do it themselves. Tailwind’s naming convention won’t let you do that. Maybe this is job security, but its also looming technical debt.&lt;/p&gt;
&lt;p&gt;If you did want to have flexibility with some class naming convention, classes would need to be named &lt;a href=&quot;/posts/truly-semantic&quot;&gt;semantically&lt;/a&gt;. As in, what the purpose of the thing is, not how it is styled since that could change over time. The purpose of the thing doesn’t change. Unfortunately, creating custom names is precisely what &lt;a href=&quot;https://v3.tailwindcss.com/docs/reusing-styles#avoiding-premature-abstraction&quot;&gt;Tailwind recommends to avoid&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Need a small utility to nudge something? That’s a deeper problem with either the design or the engineering of the component. That should be addressed with revisiting the composition, not by adding a utility to pummel the style into submission. But we don’t have time to discuss that. Add the utility, it’s what they are there for. We’ll fix it &lt;del&gt;later&lt;/del&gt; never.&lt;/p&gt;
&lt;p&gt;The fact is folks who use Tailwind aren’t thinking about responsibility and scale in this way. They are thinking how can I push this out as fast as possible. And Tailwind is great for that. Educating on best practices is always hard. In the year 2025, I still see people struggling with responsive design techniques. So setting &lt;code&gt;color-red-500&lt;/code&gt; permanently isn’t going away tomorrow and the marketing of Tailwind is going to make that exceptionally difficult. We can hope that folks realize this sooner rather than later.&lt;/p&gt;
&lt;p&gt;But I guess you could just have AI rewrite it now so no problemo?&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category></item><item><title>Avoiding tokens</title><link>https://blog.damato.design/posts/avoiding-tokens</link><guid isPermaLink="true">https://blog.damato.design/posts/avoiding-tokens</guid><description>The importance of disclosing the reasons behind decisions.</description><pubDate>Mon, 24 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The design systems community was buzzing a few days ago with the announcement of version 2 of the Salesforce Lightning Design System (SLDS). &lt;a href=&quot;https://www.linkedin.com/posts/fauxserious_lightning-design-system-2-activity-7298413367847780352-c46N&quot;&gt;I shared the link to the new documentation on LinkedIn&lt;/a&gt; and the engagement has been significant.&lt;/p&gt;
&lt;p&gt;As someone who has visited dozens of design system documentation sites, there’s certain things I tend to look for when perusing the content. One of those things is how the team handles design tokens. Design tokens are a fundamental feature of what we do, because this unlocks the ability to update the presentation of an experience with less friction. In fact, the term design tokens originated from SLDS many years ago.&lt;/p&gt;
&lt;p&gt;That’s why it was so surprising to not find a page dedicated to design tokens in the new version. The term design tokens is mostly absent from the site, except for this quote found on the front page.&lt;/p&gt;
&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://www.lightningdesignsystem.com/2e1ef8501/p/85bd85-lightning-design-system-2&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p&gt;SLDS 2 prioritizes CSS custom properties as the visual language and decreases the usage of design tokens.&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://www.lightningdesignsystem.com/2e1ef8501/p/85bd85-lightning-design-system-2&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt;SLDS 2&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/images/but-why.webp&quot; alt=&quot;Ryan Reynolds meme asking &amp;#34;But why?&amp;#34;&quot;/&gt;&lt;/p&gt;
&lt;p&gt;I aimed to find out.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-theyre-solving&quot;&gt;The problem they’re solving&lt;/h2&gt;
&lt;p&gt;Conversations sparked at the LinkedIn post as well as within the Design Systems Slack where I first saw the link. Alan Weibel, UX Architect at Salesforce, was responding in both places. In all of the discussion, his response to Brad Frost made things more clear.&lt;/p&gt;
&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7298413367847780352?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7298413367847780352%2C7298461710372270080%29&amp;replyUrn=urn%3Ali%3Acomment%3A%28activity%3A7298413367847780352%2C7298742749405622273%29&amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287298461710372270080%2Curn%3Ali%3Aactivity%3A7298413367847780352%29&amp;dashReplyUrn=urn%3Ali%3Afsd_comment%3A%287298742749405622273%2Curn%3Ali%3Aactivity%3A7298413367847780352%29&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p&gt;…I think it depends on how you define design tokens.&lt;/p&gt;&lt;p&gt;If the definition is design tokens are variables (tokens) for design system values, then yes, styling hooks are the same and come with a name-value pair.&lt;/p&gt;&lt;p&gt;If that definition comes with expectations of the specifications in W3C drafts or common implementations of design tokens, then it is not the same. They’re a completely different format that is web first, non-web second.&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7298413367847780352?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7298413367847780352%2C7298461710372270080%29&amp;replyUrn=urn%3Ali%3Acomment%3A%28activity%3A7298413367847780352%2C7298742749405622273%29&amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287298461710372270080%2Curn%3Ali%3Aactivity%3A7298413367847780352%29&amp;dashReplyUrn=urn%3Ali%3Afsd_comment%3A%287298742749405622273%2Curn%3Ali%3Aactivity%3A7298413367847780352%29&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt;Alan Weibel, Salesforce UX Architect&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;p&gt;This highlighted the issue. The team at Salesforce has considered the use of the word “design tokens” to strictly mean the artifacts coming from the &lt;a href=&quot;https://www.designtokens.org/&quot;&gt;DTCG&lt;/a&gt; work. Meanwhile, the community has more commonly considered design tokens to simply be the name-value pairing for design purposes. In other words, at Salesforce “design tokens” equals DTCG specification while outside of Salesforce “design tokens” equals name-value pairings.&lt;/p&gt;
&lt;p&gt;So they’ve decided to explicitly distance themselves from the evolving specification but that still doesn’t answer the question of “why?” Here’s another quote from Alan, this time from the Design Systems Slack:&lt;/p&gt;
&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://design-systems.slack.com/archives/C576NLX2A/p1740160012109289?thread_ts=1740077164.460439&amp;cid=C576NLX2A&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p&gt;Design tokens are very helpful for a lot of teams. They just aren’t super helpful for Salesforce. It was an extra step to create tokens to be used across multiple frameworks when 99% of Salesforce is on a single platform—the web. &lt;em&gt;Even our native mobile apps use webviews inside of the native wrapper.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We ended up using a web standard, CSS custom properties, as the name/value pair instead of design tokens. These style hooks are a lot easier to manage in both code and tooling. If folks need this CSS for a non-web application, then they can use the THEO tool.&lt;/p&gt;&lt;p&gt;In the end, people still get name/value pairs for all the frameworks but it’s less complex and easier to manage for our platform&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://design-systems.slack.com/archives/C576NLX2A/p1740160012109289?thread_ts=1740077164.460439&amp;cid=C576NLX2A&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt;Alan Weibel, Salesforce UX Architect&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;p&gt;There’s a few notes that I take away from this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It was easier for the team to write CSS Custom Properties instead of requiring a process to turn non-CSS into CSS.&lt;/li&gt;
&lt;li&gt;They opted for a solid web standard over the more tumultuous DTCG specification.&lt;/li&gt;
&lt;li&gt;The team now focused primarily on the web, so supporting other platforms was considered overkill.&lt;/li&gt;
&lt;li&gt;If some team needs to transform the CSS into non-CSS, they had an answer for that need using &lt;a href=&quot;https://github.com/salesforce-ux/theo&quot;&gt;THEO&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-fix-this&quot;&gt;How to fix this&lt;/h2&gt;
&lt;p&gt;The community had a very similar response to &lt;a href=&quot;https://www.youtube.com/watch?v=1ONxxlJnvdM&quot;&gt;Figma’s variables&lt;/a&gt;. When the new feature was announced at Config in 2023, the slide showed the word “design tokens” crossed out. Certainly meant to cause a stir amongst the crowd before announcing variables which work very similar in concept. The team made a conscious decision to not name them design tokens but why? Jacob Miller answers that question in the Design Systems Slack:&lt;/p&gt;
&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://design-systems.slack.com/archives/C56V07C4U/p1687366044434609?thread_ts=1687364523.348789&amp;cid=C56V07C4U&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p&gt;…If we tailor-made a solution for design tokens, we’d end up with disparate systems for a bunch of other features that have similar requirements, such as translation strings or conditional prototyping logic.&lt;/p&gt;&lt;p&gt;Rather than do that, we wanted two things: A more unified experience, and more alignment with code. In the same way that you can use css variables to implement design tokens, we wanted that with Figma as well.&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://design-systems.slack.com/archives/C56V07C4U/p1687366044434609?thread_ts=1687364523.348789&amp;cid=C56V07C4U&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt;Jacob Miller, Figma Product Manager&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;/posts/spicy-specifications/&quot;&gt;I wrote about why they were different&lt;/a&gt; when variables were first released, along with other design token metadata options at the time.&lt;/p&gt;
&lt;p&gt;The difference between Salesforce and Figma is that &lt;a href=&quot;https://help.figma.com/hc/en-us/articles/18490793776023-Update-1-Tokens-variables-and-styles&quot;&gt;Figma has documentation which speaks about design tokens&lt;/a&gt; and how to implement them within their ecosystem. This makes it easier for folks expecting design tokens to be supported and how to transition those folks into the specific changes within Figma to do similar things.&lt;/p&gt;
&lt;p&gt;This is where the decision for the public documentation of the SLDS breaks down. The lack of design tokens or a path for transitioning makes it hard for folks expecting to see the concept. SLDS does have name-value pairs in a similar way to how most of us think of design tokens, they’ve just renamed the concept to &lt;a href=&quot;https://www.lightningdesignsystem.com/2e1ef8501/p/319e5f-styling-hooks&quot;&gt;Styling Hooks&lt;/a&gt;. This is the same as naming your carousel component “Fred”. It might work for your internal team, but cause friction for anyone coming at it for the first time, externally or internally.&lt;/p&gt;
&lt;p&gt;I’d recommend that the SLDS team explain in their public documentation how folks looking for design tokens can transition to styling hooks. At best, define “design tokens” more generally as the community currently does or make mention in &lt;a href=&quot;https://www.salesforce.com/blog/what-are-styling-hooks/&quot;&gt;their blog post&lt;/a&gt; that the DTCG was too complex for their needs at this time.&lt;/p&gt;
&lt;h2 id=&quot;a-form-of-protest&quot;&gt;A form of protest&lt;/h2&gt;
&lt;p&gt;Ultimately, I agree with Salesforce’s decision to avoid the DTCG specification at this time. I’m a highly critical of the current direction. It is my opinion that it is scope-creeping into territories that will make it difficult for it to ever be finished.&lt;/p&gt;
&lt;p&gt;For example, the DTCG specification originally defined a color in this way:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    &amp;quot;colors&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;        &amp;quot;color-red-500&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;            &amp;quot;$type&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;color&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;            &amp;quot;$value&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;rgba(180, 216, 167, .75)&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example, the &lt;code&gt;$value&lt;/code&gt; is a string. The newer specification is &lt;a href=&quot;https://github.com/design-tokens/community-group/pull/257&quot;&gt;in the process of defining color&lt;/a&gt; in this way:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    &amp;quot;Acid green&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;        &amp;quot;$type&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;color&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;        &amp;quot;$value&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;            &amp;quot;$hex&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;#00ff66&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;            &amp;quot;$colorSpace&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;                &amp;quot;name&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;quot;srgb&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;                &amp;quot;$components&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;180&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 216&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 167&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;                &amp;quot;$alpha&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 0.75&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Among other critisms I have, seeing color defined in this way highlights why Salesforce finds “design tokens” too complicated for their customers. &lt;strong&gt;Defining all of the individual parts of a color in a structured object is tedious.&lt;/strong&gt; It is clear that the community group is less focused on making the file human-manageable at this point and expecting tools to help with the translation. I’m afraid this begins vendor lock-in where you might only be able to edit design tokens from tools. Salesforce Styling Hooks largely avoids the tooling needed to take the DTCG specification into their primary format for the web.&lt;/p&gt;
&lt;p&gt;I’ve spoken to Kaelig Deloumeau-Prigent, one of the co-chairs for the DTCG about the direction to this more complex object. The main reason seems to be the difficulty in parsing strings. His example explains that the CSS &lt;code&gt;rgb()&lt;/code&gt; color function is an example of the complexity because it takes several different kinds of syntax which any consuming application would need to support. Standarizing the format for systems to injest makes it easier. I contest that this makes it easier for the machines, not for the humans authoring tokens.&lt;/p&gt;
&lt;p&gt;I am biased in this area as the creator of &lt;a href=&quot;https://github.com/ddamato/token-operations&quot;&gt;Token Operations&lt;/a&gt;. While &lt;a href=&quot;https://github.com/ddamato/token-operations/issues/1&quot;&gt;folks could argue&lt;/a&gt; that the operations themselves are less accessible for human maintainers, the benefit is that those operations are for the machines but repeatable and sharable. The intention is for a single complex operation to be tucked away from human authors using those operations.&lt;/p&gt;
&lt;p&gt;I think a compromise in this area would be best. Where string concatenation could be added into the specification. Such that you could separate parts of the value meant to be concatenated for different platforms, but make it easier to author for humans. Based on my examples in &lt;a href=&quot;https://github.com/design-tokens/community-group/issues/224&quot;&gt;the DTCG issue&lt;/a&gt;, string concatenation would solve much of the problems that Token Operations would handle otherwise. This would allow for the original simplified construction while allowing for more complicated multi-platform values to exist. This would also finish the specification much faster by avoiding all of the complexities that might come from every individual &lt;code&gt;$type&lt;/code&gt;. Admittedly, this doesn’t solve for a zero-tooling ecosystem that Salesforce might have targetted, but I believe it would make it more accessible to those with minimal tooling.&lt;/p&gt;
&lt;h2 id=&quot;being-intentional&quot;&gt;Being intentional&lt;/h2&gt;
&lt;p&gt;Over the years, I’ve avoided the word “semantic token” to describe the middle tier due to &lt;a href=&quot;/posts/truly-semantic/&quot;&gt;my strict definition&lt;/a&gt; in favor of the word “&lt;a href=&quot;https://blog.damato.design/posts/tokens-as-intents/&quot;&gt;intent&lt;/a&gt;”. I continue to refer to this as a &lt;em&gt;type&lt;/em&gt; of design token to keep the concept connected. Where I work today, the word “design token” is rarely used in favor of “intents” which also delivers that strict definition. Where variables could be named and assigned anything, the criteria for similar as “intent” is more systematic and helps maintain a consistent construction and presentation as a result. No one asks “where are design tokens?” because &lt;em&gt;they are&lt;/em&gt; design tokens.&lt;/p&gt;
&lt;p&gt;It’s ok for design systems to be different, right? 😉&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>tokens</category><category>css</category></item><item><title>Relearning line height</title><link>https://blog.damato.design/posts/relearning-line-height</link><guid isPermaLink="true">https://blog.damato.design/posts/relearning-line-height</guid><description>Changing what I thought I knew about typesetting.</description><pubDate>Thu, 13 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My wife and I were having an argument about line-height. It’s what happens when you live in a design systems house. She was working on updating some font properties and making a case for a larger line-height reflecting on the &lt;a href=&quot;https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html&quot;&gt;WCAG guidelines 1.4.12 (Text spacing)&lt;/a&gt;. The guidelines were confusing so she also &lt;a href=&quot;https://designsystem.digital.gov/components/typography/#line-height&quot;&gt;found another reference within the United States Web Design System (USWDS)&lt;/a&gt; with the following quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Longer texts require more line height.&lt;/strong&gt; Headings and other content elements no longer than a line or two can have a line height between 1 (&lt;code&gt;line-height 1&lt;/code&gt;) and 1.35 (&lt;code&gt;line-height 3&lt;/code&gt;). Longer texts should have a line height of at least 1.5 (&lt;code&gt;line-height 4&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;I was shooketh.&lt;/strong&gt; I’ve never seen any mention of having the length of the line affect the size of the &lt;code&gt;line-height&lt;/code&gt; in the decades I’ve been working on the web. I’ve always seen it based on the &lt;code&gt;font-size&lt;/code&gt;. It’s the reason why the popular recommendation has been to use a unitless value.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.some-text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /** 150% the font-size */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    line-height&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, I immediately set out to disambiguate this and I was shocked at what I found. Read on in the comments. Just kidding.&lt;/p&gt;
&lt;p&gt;I found four sources that all mention that the &lt;code&gt;line-height&lt;/code&gt; should be affected by the length of a line; also known as the “measure”.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pimpmytype.com/line-length-line-height/&quot;&gt;Pimp My Type&lt;/a&gt; “Longer lines need more line height, shorter lines need less.”&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.typographicwebdesign.com/setting-text/font-size-line-height-measure-alignment/&quot;&gt;Typographic Web Design&lt;/a&gt; “Depending on the amount of text, the line length, the font size, and the size of a font’s x-height,  you may find that the line height needs to be tightened or loosened slightly to promote comfortable reading.”&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fonts.google.com/knowledge/using_type/choosing_a_suitable_line_height&quot;&gt;Google Fonts Knowledge&lt;/a&gt; “There’s no secret formula for setting the “right” line height for type. It’ll depend on the font, the size it’s set at, the length of the line, and the overall reading context”&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2009/08/typographic-design-survey-best-practices-from-the-best-blogs/&quot;&gt;Typographic Design Patterns And Best Practices&lt;/a&gt; “Leading (or line height) will always depend on your chosen font size and measure (or line length).”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In fact, that last article published by Smashing Magazine has some data about the typography from popular blogs. Here’s a copy of the data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;line height (pixels) ÷ body copy font size (pixels) = 1.48&lt;/li&gt;
&lt;li&gt;line length (pixels) ÷ line height (pixels) = 27.8&lt;/li&gt;
&lt;li&gt;space between paragraphs (pixels) ÷ line height (pixels) = 0.754&lt;/li&gt;
&lt;/ul&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;The article and study are from 2009. 16 years in the past from when this post is being written.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;This got my wheels turning. While I know one of the articles said there’s no perfect formula, could we make something that has a general approximation to have a variable &lt;code&gt;line-height&lt;/code&gt; based on both &lt;code&gt;font-size&lt;/code&gt; and measure? In the year 2025, let’s see what we can do.&lt;/p&gt;
&lt;h2 id=&quot;minimums-and-maximums&quot;&gt;Minimums and maximums&lt;/h2&gt;
&lt;p&gt;We’ll want to use a &lt;code&gt;clamp()&lt;/code&gt; function for a rate of change and we’ll want to know what the minimum and maximum &lt;code&gt;line-height&lt;/code&gt; values will be. If we consider that paragraphs are typically the longest lines and the recommendation is supposed to be &lt;code&gt;1.5&lt;/code&gt;, we’ll make that the maximum. Headlines are typically shorter in terms of line length and often have a &lt;code&gt;line-height&lt;/code&gt; of between &lt;code&gt;1.1&lt;/code&gt; and &lt;code&gt;1.2&lt;/code&gt; so I’ll pick &lt;code&gt;1.15&lt;/code&gt; for the minimum. We’ll set these as variables because we’ll need them later.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.15&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    line-height&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-min)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-max))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;--lh&lt;/code&gt; computes our &lt;code&gt;line-height&lt;/code&gt; depending on the width of the text container. Based on what we have so far, we’re looking to generate a value between &lt;code&gt;1.15&lt;/code&gt; and &lt;code&gt;1.5&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before we get into measuring the width, I also want to touch upon another rule of thumb. It’s usually a best practice for you to limit the number of characters for any line. This makes larger amounts of text easier to read because your eye doesn’t need to travel as far when scanning for the next line. The range of acceptable characters for this length is anywhere between 40 and 80, so I typically pick 60 characters as an average.&lt;/p&gt;
&lt;p&gt;We’ll also want to pick a minimum number of characters. This is meant to denote the least amount of characters where the &lt;code&gt;line-height&lt;/code&gt; stops decreasing if the lines are too short. For this, I’d like to determine what the longest &lt;em&gt;common&lt;/em&gt; words are and count their characters. I don’t feel like accounting for “antidisestablishmentarianism” is realistic but accounting for “acknowledgements” is certainly reasonable. I couldn’t find a readily available list of these words since what common means is subjective. However, I did find a resource that lists long words of a certain number of syllables. I reviewed the lists and chose that &lt;a href=&quot;https://www.syllablecount.com/syllables/longest/four_syllable_words&quot;&gt;4 syllable words&lt;/a&gt; feel more common than &lt;a href=&quot;https://www.syllablecount.com/syllables/longest/five_syllable_words&quot;&gt;5 syllable words&lt;/a&gt;. The average number of characters from the words listed is roughly 18, so we’ll use that as our minimum.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;Note that you can make your own logical determinations for the minimums and maximums of these numbers. It’s very likely that you might feel that the growing &lt;code&gt;line-height&lt;/code&gt; begins too early at an 18 character width and opt to start growing later.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;h2 id=&quot;measuring-measure&quot;&gt;Measuring measure&lt;/h2&gt;
&lt;p&gt;Admittedly, it won’t be feasible with CSS alone to determine the length of each line of text, but we can get the largest width of an entire containing element of text. I’d argue that we actually don’t want each individual line to affect the &lt;code&gt;line-height&lt;/code&gt; for each line differently. That would make the vertical rhythm of a single paragraph very erratic. Getting the width of a container element is more reasonable and uniform.&lt;/p&gt;
&lt;p&gt;At first, I thought a container query would be the right direction. After all, we are trying to query the width of the text. However, there’s two problems with this. First, we’d need to query for every change that happens to the container, that would be like making a container query for every pixel and that isn’t realistic. Second, you can’t affect the container within the container query. This means we’d need some wrapping element as the container first, and then we could affect the text component in some way. However, that isn’t helpful since we really want to know the text component size. So how might we do this?&lt;/p&gt;
&lt;h2 id=&quot;time-for-a-hack&quot;&gt;Time for a hack&lt;/h2&gt;
&lt;p&gt;I found a post at Frontend Masters called “&lt;a href=&quot;https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/&quot;&gt;How to Get the Width/Height of Any Element in Only CSS&lt;/a&gt;” which is precisely what we need to do. It uses a uninituitive trick with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scroll-driven_animations&quot;&gt;scroll-driven animations&lt;/a&gt; to achieve the effect. I highly recommend reading through the post if you want to learn some serious CSS magic. The tl;dr as I understand it is that as you resize an element, the amount allowed to scroll changes. That change will affect the scroll timeline and we use the amount if affects the timeline to determine the width. I could be wrong on that, I was just excited to have a working concept.&lt;/p&gt;
&lt;p&gt;For now, we want to grab the baseline of the effect with a few modifications. Let’s start with the &lt;code&gt;@&lt;/code&gt;-rules&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --_x {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  syntax: &amp;quot;&amp;lt;number&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  inherits: true;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: 0; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --ch {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  syntax: &amp;quot;&amp;lt;integer&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  inherits: true;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: 0; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@keyframes&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; x { to { --_x&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;} }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is mostly identical to what is in the post, however I’ve changed the &lt;code&gt;--w&lt;/code&gt; variable to &lt;code&gt;--ch&lt;/code&gt;. This will represent the number of characters or the width of the containing element in terms of &lt;code&gt;ch&lt;/code&gt;. Now for the declaration block:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --lh-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.15&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --lh-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  --ch&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(1 &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--_x))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  display&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; inline-block&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  overflow&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; auto&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  timeline-scope&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; --cx&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  animation&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; x linear&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  animation-timeline&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; --cx&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  animation-range&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; entry 100&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; exit 100&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  line-height&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-min)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;    var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-max)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;  )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;:before {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    content: &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    width&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;ch&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    display&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; block&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    view-timeline&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; --cx inline&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few things are changed here, other than removing references to the y-axis. First, I’ve removed the &lt;code&gt;position&lt;/code&gt; properties because that can create new &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context&quot;&gt;stacking contexts&lt;/a&gt; and it doesn’t seem to change the final outcome. I’ve also set the &lt;code&gt;.text&lt;/code&gt; to be &lt;code&gt;inline-block&lt;/code&gt;. This is because block elements will just take up the width of their container. We want the amount of text to determine the size of the container. I’ve also updated &lt;code&gt;::before&lt;/code&gt; to have &lt;code&gt;width: 1ch&lt;/code&gt; which allows us to measure in terms of characters instead of pixels.&lt;/p&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;Note that if this text element is in a flex container, you’ll want to set &lt;code&gt;align-self: start&lt;/code&gt; here also so this flex child doesn’t stretch and act like a block element. I’d even recommend that you use flex containers because making all text elements behave as inline will cause weird wrapping for shorter text. Using flex will ensure elements stack as expected.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;Now we’re ready for the final part, math!&lt;/p&gt;
&lt;h2 id=&quot;equation-of-a-line&quot;&gt;Equation of a line&lt;/h2&gt;
&lt;p&gt;Like my previous post that explored some &lt;a href=&quot;/posts/two-typographic-tricks&quot;&gt;advanced typographic tricks&lt;/a&gt;, we’re going to need to determine the rate of change here. We’re working linearly so we’ll need the same equations as before. First, the slope of the line.&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;m=(y_2-y_1)/(x_2-x_1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can represent this in CSS like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.15&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 60&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-delta&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-max) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-delta&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-max) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --slope&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-delta) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-delta))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where &lt;code&gt;--slope&lt;/code&gt; is the result. Next, we need to solve for the “b” part of equation. This is what I’ll call the &lt;code&gt;--offset&lt;/code&gt;.&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;b=y_1 - (m \times x_1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7778em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That is represented in CSS like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.15&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 60&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-delta&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-max) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-delta&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-max) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --slope&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-delta) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-delta))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --offset&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-min) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--slope) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, we’ll use our restructured line equation to determine what the &lt;code&gt;line-height&lt;/code&gt; value should be based on the &lt;code&gt;--ch&lt;/code&gt; value.&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x=(y-b)/m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.text&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.15&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.5&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-min&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-max&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 60&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh-delta&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-max) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ch-delta&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-max) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --slope&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-delta) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-delta))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --offset&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch-min) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--slope) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--lh-min))&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --lh&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ch) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--offset)) / &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--slope)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Putting these variables together with the earlier CSS gives you the demo below.&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;CSS-Only, super easy screensize numeric pixel value width/height vars&quot; src=&quot;https://codepen.io/fauxserious/embed/VYwYjJx?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/VYwYjJx&quot;&gt;
CSS-Only, super easy screensize numeric pixel value width/height vars&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;I’ve included a character counter at the end of the element so you can see the result of &lt;code&gt;--ch&lt;/code&gt;. I’ve also made the element &lt;code&gt;contenteditable&lt;/code&gt; so you can change the text right in the browser window. You should see the line height change with a &lt;code&gt;ch&lt;/code&gt; value clamped between &lt;code&gt;18&lt;/code&gt; and &lt;code&gt;60&lt;/code&gt;.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category><category>typography</category></item><item><title>Mixinimation</title><link>https://blog.damato.design/posts/mixinimation</link><guid isPermaLink="true">https://blog.damato.design/posts/mixinimation</guid><description>An absolute mad-scientist method for reusing a collection of styles.</description><pubDate>Thu, 30 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;Warning, you probably don’t want to what this post describes anywhere. It’s for funsies.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;In speaking with a colleague, we were discussing solutions for people who were unable to use a framework’s custom component that still expected to have the styles for that component. Think of how you might have a React &lt;code&gt;&amp;lt;Button/&amp;gt;&lt;/code&gt; that would need to be visually similar to some static HTML page &lt;code&gt;&amp;lt;button/&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Folks writing CSS normally should quickly identify that placing all of the necessary styles under a single identifiable selector that can be applied to the element is the normal way of getting the job done.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.my-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /** Reusable styles for the button here */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;my-button&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;Click me&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This assumes that the styles for &lt;code&gt;.my-button&lt;/code&gt; are found on the same page as the HTML &lt;code&gt;&amp;lt;button class=&amp;quot;my-button&amp;quot;/&amp;gt;&lt;/code&gt;. This is a reasonable method for having reusable styles.&lt;/p&gt;
&lt;p&gt;But maybe you don’t have the ability to update the HTML, how would you change the CSS such that the buttons would receive the styles? What you should first land on is updating the selector to be more generic:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /** Reusable styles for the button here */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, it’s possible that we don’t want to affect &lt;em&gt;all&lt;/em&gt; buttons this way. Specifically, we want to have some reusable styles that are written for the common &lt;code&gt;&amp;lt;Button/&amp;gt;&lt;/code&gt; component, but we also want to take those same styles and create a new stylesheet specific for when we can’t access the HTML. In other words, we don’t want to write the same styles twice, but we do want to change what those styles target. How could we do this?&lt;/p&gt;
&lt;p&gt;If you’re familiar with the CSS tooling ecosystem, you might suggest using a &lt;a href=&quot;https://sass-lang.com/guide/#mixins&quot;&gt;&lt;code&gt;@mixin&lt;/code&gt; syntax made popular by Sass&lt;/a&gt;. That will allow the styles to be written once, and be reused underneath different selectors.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@mixin&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; my-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;    /** Reusable styles for the button here */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** In the &amp;lt;Button/&amp;gt; component styles */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.my-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    @include&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; my-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** In the restricted HTML styles */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    @include&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; my-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, this is not native CSS syntax &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/9350#issuecomment-1939628591&quot;&gt;yet&lt;/a&gt;. So, this assumes that you are using a preprocessor to transform the &lt;code&gt;@mixin&lt;/code&gt; syntax into native CSS that the browser can parse. Though, I was curious; is there a way we can get mixin behavior in CSS today? And I think there is…&lt;/p&gt;
&lt;h2 id=&quot;keyframes&quot;&gt;Keyframes&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;@keyframes&lt;/code&gt; syntax is a method to add a collection of styles to an element. Typically, we’d define blocks of declarations for each point in the keyframe timeline. We could define all of these styles as the &lt;code&gt;from&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; points, causing these styles to be applied for the entire duration of the animation.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@keyframes&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; mixin-button {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    from, to { &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** Reusable styles */&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we can write the animation value as the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.my-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    animation&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; mixin-button 0.001&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; forwards&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;animation&lt;/code&gt; shorthand has three values. The first is the &lt;code&gt;animation-name&lt;/code&gt; value which refers to the &lt;code&gt;@keyframes&lt;/code&gt; name set earlier. The next is the &lt;code&gt;animation-duration&lt;/code&gt; which cannot be &lt;code&gt;0&lt;/code&gt;, the default. Setting to a very small value causes the animation to be nearly immediate. The &lt;code&gt;animation-fill-mode&lt;/code&gt; is set to forwards to ensure that the styles provided by the animation continue past the end of the animation so they persist after the very short duration.&lt;/p&gt;
&lt;p&gt;Now remembering to write those three values in the shorthand can be a bit cumbersome, so instead we can hide all of this in a CSS custom property.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --mixin-button {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  syntax: &amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;mixin-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.001ms&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; forwards;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to use the following syntax to apply all of the styles using a single property-value pair under a selector.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** In the &amp;lt;Button/&amp;gt; component styles */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.my-button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    animation&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--mixin-button)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** In the restricted HTML styles */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    animation&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--mixin-button)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s the whole implementation working in Codepen:&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Mixinimation&quot; src=&quot;https://codepen.io/fauxserious/embed/YPKgwJj/ab3653ad281c25f70299076c52a2c7f5?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/YPKgwJj/ab3653ad281c25f70299076c52a2c7f5&quot;&gt;
Mixinimation&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;em&gt;&amp;lt;Laughs maniacally into his computer&amp;gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;drawbacks&quot;&gt;Drawbacks&lt;/h2&gt;
&lt;p&gt;Ignoring the hacky nature of applying all the styles by hiding them underneath the &lt;code&gt;animation&lt;/code&gt; property, one drawback to this will also be where actual animations would be expected for these elements. While there is a syntax to apply multiple animations to a single element, I’m not sure how these might conflict. Additionally, if you are using a &lt;a href=&quot;https://davidwalsh.name/detect-node-insertion&quot;&gt;node detection script&lt;/a&gt;, the animation would fire for all of these elements if listening for animations.&lt;/p&gt;
&lt;p&gt;My recommendation: try it out, put it in the dark corner of your toolbox but, avoid reaching for this unless you really have challenging constraints. We’re all waiting patiently for more native solutions and with CSS moving fast, it’ll be here sooner than we realize.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>css</category></item><item><title>Interoperable tokens</title><link>https://blog.damato.design/posts/interoperable-tokens</link><guid isPermaLink="true">https://blog.damato.design/posts/interoperable-tokens</guid><description>Exploring a way to make token importing similar in different contexts.</description><pubDate>Thu, 16 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In &lt;a href=&quot;https://system.damato.design&quot;&gt;my design system playground&lt;/a&gt;, one of my small goals was to have the ability to import a design token identically between CSS and JavaScript. Here’s an example of the developer experience I was expecting and succeed in making: to use &lt;code&gt;tokens.$action_primary_backgroundColor&lt;/code&gt; as a value reference in both &lt;code&gt;.scss&lt;/code&gt; and &lt;code&gt;.js&lt;/code&gt; files identically.&lt;/p&gt;
&lt;h3 id=&quot;writing-scss&quot;&gt;Writing (S)CSS.&lt;/h3&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@use&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;../tokens.module&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;:where&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;data-priority&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;quot;primary&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;]) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    background-color&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; tokens.$action_primary_backgroundColor&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;writing-jsx&quot;&gt;Writing JS(X).&lt;/h3&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; tokens &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt; &amp;#39;./_tokens.module.scss&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; PrimaryButton&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(props) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;button&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        {&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;props}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        style&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;{{ &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;            backgroundColor&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; tokens&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;.$action_primary_backgroundColor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;        }}/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part is that the intention of using the background color for primary buttons is shared between the CSS and the JavaScript. &lt;code&gt;tokens.$action_primary_backgroundColor&lt;/code&gt; is identical between these examples while being applied in different languages. This method helps reduce the context switching between writing CSS and writing JS to get the expected styling value.&lt;/p&gt;
&lt;h2 id=&quot;working-source&quot;&gt;Working source&lt;/h2&gt;
&lt;p&gt;I’ll cut to the chase, the following is a &lt;code&gt;_tokens.module.scss&lt;/code&gt; file that all the components have access to. &lt;a href=&quot;https://github.com/damato-design/system/blob/main/src/components/_tokens.module.scss&quot;&gt;Here’s the file I use in my system.&lt;/a&gt; You’ll want to generate this file based on how you create and manage tokens in your system. You should expect many, many more entries in this file than what is shown below. This example is only supporting &lt;code&gt;tokens.$some_token&lt;/code&gt; between &lt;code&gt;.scss&lt;/code&gt; and &lt;code&gt;.js&lt;/code&gt; files.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;scss&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** For .scss, $some_token does not need to match the --some_token  */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;$some_token&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(--some_token);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** For .js, the #{&amp;#39;$some_token&amp;#39;} needs match $some_token */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;:export {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    #{&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;&amp;#39;$some_token&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; $some_token&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;As you might tell, there are some requirements to this in order for the DX to work well.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You’ll need to ensure that the CSS custom property &lt;code&gt;--some_token&lt;/code&gt; has a value and is present on each page such that &lt;code&gt;var(--some_token)&lt;/code&gt; is later resolved. You could opt to &lt;em&gt;not&lt;/em&gt; use a CSS custom property here if the value don’t change. However, if you have or plan to have dark mode in your system, then you don’t want to hardcode the values here.&lt;/li&gt;
&lt;li&gt;You’ll need to be using &lt;a href=&quot;https://github.com/css-modules/css-modules&quot;&gt;CSS modules&lt;/a&gt; to get the JS import working, using the &lt;code&gt;:export&lt;/code&gt; pseudo selector. Most JS frameworks support CSS modules with minimal configuration. The word &lt;code&gt;module&lt;/code&gt; in the file name is required for the CSS Modules processing to pick this up as a CSS Module, so it can be imported into &lt;code&gt;.js&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;You’ll need to be using &lt;a href=&quot;https://sass-lang.com/&quot;&gt;SCSS&lt;/a&gt; to get the &lt;code&gt;@use&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; variable syntax working in your &lt;code&gt;.scss&lt;/code&gt; files. The leading &lt;code&gt;_&lt;/code&gt; in the file name &lt;code&gt;_tokens.module.scss&lt;/code&gt; is required for the &lt;code&gt;@use&lt;/code&gt; syntax to work. This will expose a &lt;code&gt;tokens&lt;/code&gt; key in the SCSS &lt;a href=&quot;https://sass-lang.com/documentation/at-rules/use/&quot;&gt;based on the file name&lt;/a&gt;. In other words, to get the &lt;code&gt;tokens.&lt;/code&gt; syntax in your files, your file name should start with &lt;code&gt;_tokens.&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;required-characters&quot;&gt;Required characters&lt;/h2&gt;
&lt;p&gt;There’s lots of token naming constructions out there that delimit the sections of the token. For this to work, the underscore (&lt;code&gt;_&lt;/code&gt;) is one of the only characters that will work between (S)CSS and JS.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The dot delimiter (&lt;code&gt;.&lt;/code&gt;) will not be valid in &lt;code&gt;.scss&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;The hyphen delimiter (&lt;code&gt;-&lt;/code&gt;) will not be valid in &lt;code&gt;.js&lt;/code&gt; files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you generate the &lt;code&gt;_tokens.module.scss&lt;/code&gt; file, you’ll want to convert any delimiter to an underscore. As an example, if you have a token &lt;code&gt;button.primary.backgroundColor&lt;/code&gt;, you’ll want to prepare that as &lt;code&gt;button_primary_backgroundColor&lt;/code&gt; to be used in source files. This makes the name usable between &lt;code&gt;.scss&lt;/code&gt; and &lt;code&gt;.js&lt;/code&gt; files in an identical way.&lt;/p&gt;
&lt;p&gt;The leading &lt;code&gt;$&lt;/code&gt; is expected for the SCSS ecosystem to be used as a variable. This is carried over into the JS ecosystem for consistency.&lt;/p&gt;
&lt;p&gt;You can review the source of my system to find additional details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/damato-design/system/blob/main/src/modes/_system.yml&quot;&gt;How I curate tokens with values in &lt;code&gt;.yaml&lt;/code&gt;.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/damato-design/system/blob/main/scripts/generate-module.js&quot;&gt;How I transform tokens to &lt;code&gt;_tokens.module.scss&lt;/code&gt;.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>tokens</category><category>css</category><category>naming</category></item><item><title>Your first component</title><link>https://blog.damato.design/posts/your-first-component</link><guid isPermaLink="true">https://blog.damato.design/posts/your-first-component</guid><description>The reason why it is almost always a button</description><pubDate>Fri, 10 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://bsky.app/profile/cory.laviska.com&quot;&gt;Cory LaViska&lt;/a&gt; of &lt;a href=&quot;https://shoelace.style/&quot;&gt;Shoelace&lt;/a&gt; fame posted this a few days ago:&lt;/p&gt;
&lt;blockquote class=&quot;blockquote&quot; cite=&quot;https://bsky.app/profile/cory.laviska.com/post/3lfczce754k2d&quot; data-astro-cid-arj5dyob&gt; &lt;div class=&quot;quote&quot; data-astro-cid-arj5dyob&gt; &lt;p&gt;when building a component library, what component do you build first and why is it always button?&lt;/p&gt; &lt;/div&gt; &lt;span data-astro-cid-arj5dyob&gt;— &lt;a href=&quot;https://bsky.app/profile/cory.laviska.com/post/3lfczce754k2d&quot; data-astro-cid-arj5dyob&gt; &lt;cite data-astro-cid-arj5dyob&gt;Cory LaViska&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt; &lt;/blockquote&gt;
&lt;p&gt;It’s a similar question that starts &lt;a href=&quot;https://www.youtube.com/watch?v=PkHCk36hMik&quot;&gt;Dan Mall’s talk he first performed at Clarity 2022&lt;/a&gt;. Dan argues that the button doesn’t provide impact to teams; buttons are easy and obvious. It would be more useful to determine what component would be the most impactful and then contribute that to the business. In Dan’s talk, he uses the example of a map component for Uber which is essential to the experience and wildly complex. I disagree that the map is a good choice for the design systems team to tackle, but I agree with the sentiment of prioritizing impact.&lt;/p&gt;
&lt;p&gt;However, for folks that are dipping their toes into design systems, Cory’s question is interesting. Why is the button the first thing most folks build?&lt;/p&gt;
&lt;h2 id=&quot;popularity&quot;&gt;Popularity&lt;/h2&gt;
&lt;p&gt;I was having a dinner conversation with &lt;a href=&quot;https://www.adekunleoduye.com/&quot;&gt;Adekunle Oduye&lt;/a&gt; one evening where I was asking him a similar question; how do we choose what components belong in a design system? For many folks, we’d look at existing systems and inventory what the most popular components are. Looking at &lt;a href=&quot;https://component.gallery/&quot;&gt;component.gallery&lt;/a&gt;, Button is the most popular component with 141 examples at the time of this writing. It would make sense that because it is the most popular component, it probably belongs in your system too.&lt;/p&gt;
&lt;p&gt;But this should introduce a new question. When the first design systems were starting, how did &lt;em&gt;they&lt;/em&gt; know they needed these components? The answer is doing their own inventory local to their companies’ needs. In fact, that’s ultimately what Dan is suggesting in his talk. The design system should be a reflection of the people it is made for. So what components you maintain should be determined by an internal inventory of usage.&lt;/p&gt;
&lt;p&gt;It is easy to tie impact to how often something is needed. The more it is needed, the more it impacts the final result. Therefore, because the button tends to be the most popular, we tend to want to build it first.&lt;/p&gt;
&lt;h2 id=&quot;necessity&quot;&gt;Necessity&lt;/h2&gt;
&lt;p&gt;Since we want interactive websites, we expect that we will need a button at the very least, even if that button operates as a link. Without interactivity, a website is a one-sided conversation. There’s no navigation, no feedback. It might as well be a piece of paper to be read once and maybe bookmarked for later. To begin turning a web page into something a little more, that interactivity is needed.&lt;/p&gt;
&lt;p&gt;Importantly, I don’t believe small sites need design systems. In fact, most of the personal websites I’ve created don’t have a design system; just some reusable styles. Larger sites beget interactivity to traverse between the homepage, dashboard, products, checkout, and beyond. Each one of those pages is expecting a user to achieve some goal. To do that, the user needs to make choices, and those choices are coded as moments of interactivity within buttons, among other controls.&lt;/p&gt;
&lt;h2 id=&quot;complexity&quot;&gt;Complexity&lt;/h2&gt;
&lt;p&gt;So far, I’ve been making a case for why you’ll probably need a button in your system. I haven’t yet touched upon why it’s most likely first.&lt;/p&gt;
&lt;p&gt;The button component is deviously complex in the number of states and priorities it could be meant to convey. When we alter a single presentational attribute, it could convey a whole new meaning to the user. This is especially apparent by the way we’ve landed on our common naming system for buttons. In other words, &lt;strong&gt;the button is the only interactive control that can convey priority&lt;/strong&gt;. There is no such thing as a “primary” text field. As an example, the primary button is meant to guide the user to the most likely direction they should take.&lt;/p&gt;
&lt;p&gt;Getting all the visual treatments right such that they convey the correct information can be a daunting exercise for folks unfamilar with the permutations. However, this is also the &lt;em&gt;only&lt;/em&gt; hard part about the button. There’s really only one thing the button can do, accept a click. So we can focus our attention on the visual treatment alone. Which leads to the next reason.&lt;/p&gt;
&lt;h2 id=&quot;containment&quot;&gt;Containment&lt;/h2&gt;
&lt;p&gt;The button is mostly often considerd an “atomic” component, meaning it is commonly not broken down to further elements. Many other components are composed of other pieces and with each additional piece added to a component, more complexity is added. So not only would a person need to make decisions on the visual treatment of each part, you’d also need to make decisions on how these pieces work between themselves and in some cases within the larger composition they’ll be set within.&lt;/p&gt;
&lt;p&gt;Since the button is self-contained and has no real complexity past the various visual treatments, we tend to tackle this low-hanging fruit first as a quick win.&lt;/p&gt;
&lt;h2 id=&quot;tokens&quot;&gt;Tokens&lt;/h2&gt;
&lt;p&gt;The final note to mention is that we often spend a great deal of time curating tokens, either by choosing names or their values. In order to test our architectures, it would be most helpful to assign these tokens to something that can show coverage easily. The button having lots of potential in visual complexity and customization, while being a small self-contained component, makes it a excellent “test” component to build where you can curate your token architectures and check the visual result in something you have confidence will appear on any page.&lt;/p&gt;
&lt;p&gt;So all of these things contribute to the reason why I believe the button tends to be first in our libraries. In &lt;a href=&quot;https://system.damato.design/&quot;&gt;my system playground&lt;/a&gt;, it was probably the first “non-primitive” component.&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>components</category><category>tokens</category></item><item><title>Two Typographic Tricks</title><link>https://blog.damato.design/posts/two-typographic-tricks</link><guid isPermaLink="true">https://blog.damato.design/posts/two-typographic-tricks</guid><description>Modern CSS unlocks some wishful visual treatments.</description><pubDate>Thu, 19 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been writing CSS for a long time and it’s really exciting to see the fast improvements to the language that we’ve received over that time. In this post, I’ll demonstrate two ideas that I always wanted to do natively with CSS alone.&lt;/p&gt;
&lt;p&gt;First, a warm-up before we get to the wild stuff.&lt;/p&gt;
&lt;h2 id=&quot;grid-centered-line-height&quot;&gt;Grid centered line-height&lt;/h2&gt;
&lt;p&gt;I know of plenty of designers who want to maintain a &lt;a href=&quot;https://zellwk.com/blog/why-vertical-rhythms/&quot;&gt;vertical rhythm&lt;/a&gt; for all content on the page. They’ll painstakingly adjust border widths and line-heights to ensure all of the little elements on the page align to some vertical grid such that there’s a rhythm to reading downward.&lt;/p&gt;
&lt;p&gt;As the person who wrote &lt;a href=&quot;https://gridless.design/&quot;&gt;Gridless Design&lt;/a&gt;, you can imagine how I feel about this. In my opinion, the time spent trying to get this alignment would be more worthwhile on other things; say… improving the user experience as an example. However, now that we have some new CSS in browsers, getting the vertical rhythm to align with your grid can happen at any font size very easily.&lt;/p&gt;
&lt;p&gt;We’re going to need a bit of math for this. Let’s say that you have an &lt;a href=&quot;https://notadesigner.io/p/8px-grid&quot;&gt;&lt;code&gt;8px&lt;/code&gt; grid&lt;/a&gt;. This means elements on your composition should be sized in multiples of &lt;code&gt;8px&lt;/code&gt;. Let’s also say that your &lt;code&gt;line-height&lt;/code&gt; for paragraphs should be &lt;code&gt;1.4&lt;/code&gt;, meaning the resulting height of each line should be 1.4 times the size of the font so the space between lines helps readability. If your paragraph font is &lt;code&gt;18px&lt;/code&gt;, this means the paragraph &lt;code&gt;line-height&lt;/code&gt; would result in each line of text being &lt;code&gt;25.2px&lt;/code&gt; high.&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;18&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;mn&gt;1.4&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;25.2&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;18*1.4=25.2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;∗&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.4&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;25.2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Clearly, this doesn’t align to our &lt;code&gt;8px&lt;/code&gt; grid. Historically, we’d curate the &lt;code&gt;line-height&lt;/code&gt; each time we introduce a new &lt;code&gt;font-size&lt;/code&gt;. In this case, we have to hardcode the &lt;code&gt;line-height&lt;/code&gt; knowing the &lt;code&gt;font-size&lt;/code&gt; such that it remains on the &lt;code&gt;8px&lt;/code&gt; grid.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.paragraph&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    font-size&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    line-height&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--paragraph-lineheight)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;rounding-to-the-nearest-grid&quot;&gt;Rounding to the nearest grid&lt;/h3&gt;
&lt;p&gt;In order to determine the correct number here, we’ll need to round 25.2 to the nearest 8. Doing this in JavaScript would look like this, assuming the numbers are entered without units:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; lineHeight&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;(fontSize &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 18&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; lhTarget &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.4&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; gridUnit &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 8&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; Math&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.ceil&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;((fontSize &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; lhTarget) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; gridUnit) &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; gridUnit;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The messy part is rounding to the nearest grid unit. We have to divide 25.2 by 8, then round that to the nearest integer and then multiply by 8 to get 32. This means that the &lt;code&gt;--paragraph-lineheight&lt;/code&gt; should be set as &lt;code&gt;32px&lt;/code&gt; when the font size is &lt;code&gt;18px&lt;/code&gt; and the target line height is &lt;code&gt;1.4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As you can see, this is very cumbersome. We’d need to know the exact &lt;code&gt;font-size&lt;/code&gt; value, the grid unit, and the target &lt;code&gt;line-height&lt;/code&gt; for this kind of element to determine the final result.&lt;/p&gt;
&lt;p&gt;Luckily, the new &lt;code&gt;round()&lt;/code&gt; CSS function can really help us out.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.paragraph&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    line-height&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; round(up&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.4&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--grid-unit&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 8&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is effectively the same calculation that was demonstrated earlier in JavaScript. The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/round&quot;&gt;&lt;code&gt;round()&lt;/code&gt; function&lt;/a&gt; is &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/round#browser_compatibility&quot;&gt;fairly well supported&lt;/a&gt; and can take three arguments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first can determine the rounding strategy, in this case we want to round up in the same way we used &lt;code&gt;Math.ceil()&lt;/code&gt; before.&lt;/li&gt;
&lt;li&gt;The next argument is the number we’re going to be rounding. This will represent the target &lt;code&gt;line-height&lt;/code&gt;; basically the minimum &lt;code&gt;line-height&lt;/code&gt; allowed. Unfortunately, we can’t use the unitless &lt;code&gt;line-height&lt;/code&gt; here but we can use the &lt;code&gt;em&lt;/code&gt; version instead which would result in the same final pixel amount.&lt;/li&gt;
&lt;li&gt;Finally, the amount to round by. This part of the function is &lt;strong&gt;awesome&lt;/strong&gt;. It avoids all of the weird division and multiplication to round by an amount in the JavaScript. Here, we want to round to the nearest grid unit. In this example, I simply set the default as &lt;code&gt;8px&lt;/code&gt; in the second argument of the &lt;code&gt;var()&lt;/code&gt;, and then it can accept any update in the page and fallback to &lt;code&gt;8px&lt;/code&gt;. You can also use any value with units here instead.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You’ll also notice I’m not explicitly setting the &lt;code&gt;font-size&lt;/code&gt; here. This is because this &lt;code&gt;line-height&lt;/code&gt; value will respect whatever &lt;code&gt;font-size&lt;/code&gt; this element is because it’s referencing &lt;code&gt;em&lt;/code&gt; units, even if it is inherited from a parent! If your target &lt;code&gt;line-height&lt;/code&gt; is meant to be tighter, as is normally the case for headings, you can adjust the target to be smaller like &lt;code&gt;1.2em&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.heading&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    line-height&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; round(up&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1.2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--grid-unit&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 8&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is a codepen demonstrating the technique. The gray lines behind the text represent the grid unit intervals and the red background on the text shows how much space the text takes up. You’ll see that the gray lines will never be cut-off at the bottom of the container because the &lt;code&gt;line-height&lt;/code&gt; for this element is &lt;em&gt;always&lt;/em&gt; on grid. The span is not important to the CSS, it’s there just to visualize the height of the text.&lt;/p&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Line Height on grid&quot; src=&quot;https://codepen.io/fauxserious/embed/wBwdwNa?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/wBwdwNa&quot;&gt;
Line Height on grid&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;Ok, that was a good warm-up. Now it’s time to get wild!&lt;/p&gt;
&lt;h2 id=&quot;dynamic-letter-spacing&quot;&gt;Dynamic letter-spacing&lt;/h2&gt;
&lt;p&gt;Like &lt;code&gt;line-height&lt;/code&gt;, I always felt that tweaking the &lt;code&gt;letter-spacing&lt;/code&gt; was a waste of time. Especially as more custom fonts become normal, I felt setting the &lt;code&gt;letter-spacing&lt;/code&gt; should be embedded in the font file itself. However, I saw &lt;a href=&quot;https://www.linkedin.com/posts/badimon_theres-this-strange-perception-effect-where-activity-7267563944435757056-BhJv/#&quot;&gt;a post by Mathieu Badimon&lt;/a&gt;, formerly leading efforts for the design system at Adobe, sharing a way to do this &lt;em&gt;dynamically&lt;/em&gt;. The gist is that he’s developed a scale where the &lt;code&gt;letter-spacing&lt;/code&gt; of the text will get tighter as the &lt;code&gt;font-size&lt;/code&gt; increases. Immediately, I wanted to try to do this in CSS alone. This took much longer than I hoped.&lt;/p&gt;
&lt;h3 id=&quot;the-easy-parts&quot;&gt;The easy parts&lt;/h3&gt;
&lt;p&gt;The first thing is that there is an upper and lower limit. This is pretty easy in CSS today using the &lt;code&gt;clamp()&lt;/code&gt; function. So we’ll start with that using the values he recommends in the post; Sizes &lt;code&gt;25px&lt;/code&gt; and below should be &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;-4%&lt;/code&gt; for sizes above about &lt;code&gt;145px&lt;/code&gt;. Since, &lt;code&gt;letter-spacing&lt;/code&gt; doesn’t accept &lt;code&gt;%&lt;/code&gt; units, we’ll use &lt;code&gt;em&lt;/code&gt; instead which can be thought of as a percent of the &lt;code&gt;font-size&lt;/code&gt;. Remember that the lowest value goes first, that’s why &lt;code&gt;-0.04em&lt;/code&gt; is first and &lt;code&gt;0em&lt;/code&gt; is last.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.paragraph&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    letter-spacing&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        -0.04&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ems) * 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, so far so good. Now all we need is the rate of change. You’ll see that there’s a graph included in his post, but he mentions that the graph is actually inaccurate. That’s fine, we’re going to use the numbers &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:ugcPost:7267563941390684160?commentUrn=urn%3Ali%3Acomment%3A%28ugcPost%3A7267563941390684160%2C7268482417206689792%29&amp;replyUrn=urn%3Ali%3Acomment%3A%28ugcPost%3A7267563941390684160%2C7268561421481738240%29&amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287268482417206689792%2Curn%3Ali%3AugcPost%3A7267563941390684160%29&amp;dashReplyUrn=urn%3Ali%3Afsd_comment%3A%287268561421481738240%2Curn%3Ali%3AugcPost%3A7267563941390684160%29&quot;&gt;he provided later in a comment&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/dynamic-letter-spacing-chart.png&quot; alt=&quot;Dynamic Letter Spacing Chart&quot;/&gt;&lt;/p&gt;
&lt;p&gt;What we want is the rate of change between to points, this can be thought of as finding the slope of a line between points. If we think of the &lt;code&gt;font-size&lt;/code&gt; as the &lt;code&gt;y&lt;/code&gt; and the resulting percent as &lt;code&gt;x&lt;/code&gt;, we can find the slope (&lt;code&gt;m&lt;/code&gt;) in the following way:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;m=(y_2-y_1)/(x_2-x_1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Plugging in some numbers from the chart, we get the following:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;28&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;25&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;.1&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;30&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;m=(28-25)/(-.1-0)=-30&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;30&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This shouldn’t be a surprise from Mathieu’s post:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/letter-spacing-steps.jpg&quot; alt=&quot;Letter spacing steps&quot;/&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-30&lt;/code&gt; is related to the &lt;code&gt;30px&lt;/code&gt; every &lt;code&gt;1%&lt;/code&gt; decrease. The next thing we need is the formula that produces all of the &lt;code&gt;x&lt;/code&gt; values based on a given &lt;code&gt;y&lt;/code&gt;. You might remember this formula from school:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y=mx+b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, we want to solve for &lt;code&gt;x&lt;/code&gt;, so we need to rearrange the terms:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x=(y-b)/m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this formula, &lt;code&gt;y&lt;/code&gt; is our &lt;code&gt;font-size&lt;/code&gt;, and &lt;code&gt;m&lt;/code&gt; is the slope we got earlier of &lt;code&gt;-30&lt;/code&gt;. So what does &lt;code&gt;b&lt;/code&gt; represent? It’s where the slope crosses the x-axis. We could find this by moving terms around again, but Mathieu has already provided this number; &lt;code&gt;25&lt;/code&gt; (for &lt;code&gt;25px&lt;/code&gt; where this calculation starts). Our formula now looks like this:&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.css&quot; integrity=&quot;sha384-WsHMgfkABRyG494OmuiNmkAOk8nhO1qE+Y6wns6v+EoNoTNxrWxYpl5ZYWFOLPCM&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;p&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;25&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;30&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;percent=(fontsize-25)/-30&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8095em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;erce&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;ze&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;30&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This doesn’t look too intimidating yet, but here’s where it get challenging. We need to turn this into CSS. Let’s try just making some variables for this first:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.paragraph&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 25&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** starting size */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --m&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; -30&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** rate of change */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --x&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--b)) / &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--m)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** percent? */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    letter-spacing&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        -0.04&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ems) * 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, so far all of this &lt;em&gt;should&lt;/em&gt; work. We’re still missing the &lt;code&gt;--ems&lt;/code&gt; assignment but we can subtract unit values from each other, and we can divide a unit number by an unitless number. When we subtract units like this, the result is resolved into a pixel amount. This’ll be important later.&lt;/p&gt;
&lt;h3 id=&quot;the-hard-parts&quot;&gt;The hard parts&lt;/h3&gt;
&lt;aside data-astro-cid-duqfclob&gt; &lt;div class=&quot;content&quot; data-astro-cid-duqfclob&gt; &lt;p&gt;We’re about to begin using super new CSS properties in a very hacky way. Consider yourself warned.&lt;/p&gt; &lt;/div&gt; &lt;/aside&gt;
&lt;p&gt;The first problem is that &lt;code&gt;--x&lt;/code&gt; &lt;em&gt;isn’t&lt;/em&gt; a unitless value, it’s a pixel amount. Setting this as the &lt;code&gt;--ems&lt;/code&gt; isn’t correct because in Mathieu’s formula, we want a &lt;em&gt;percent&lt;/em&gt; of the &lt;code&gt;1em&lt;/code&gt;. So we want &lt;code&gt;--x&lt;/code&gt; to have the pixel units removed.&lt;/p&gt;
&lt;p&gt;There’s &lt;a href=&quot;https://dev.to/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j&quot;&gt;a recent trick made popular by Jane Ori&lt;/a&gt; which demonstrates how we might hack CSS to give us unitless values using &lt;code&gt;atan2()&lt;/code&gt;. We’ll need to add a few things:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --x {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  syntax: &amp;quot;&amp;lt;length&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: 0px;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.paragraph&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 25&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** starting size */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --m&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; -30&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** rate of change */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --x&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--b)) / &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--m)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** percent? */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    letter-spacing&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        -0.04&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ems) * 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing we need is to effectively &lt;em&gt;force&lt;/em&gt; the &lt;code&gt;--x&lt;/code&gt; to give us a pixel amount. It might seem weird that we need to do this since we know it’s resolving as a pixel, but it’ll be important so we can use &lt;code&gt;atan2()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --x {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  syntax: &amp;quot;&amp;lt;length&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: 0px;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.paragraph&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 25&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** starting size */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --m&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; -30&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** rate of change */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --x&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--b)) / &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--m)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** percent? */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ems&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; tan&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;atan2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--x)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 16&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** percent! */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    letter-spacing: &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        -0.04&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ems) * 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll see I’ve added the &lt;code&gt;--ems&lt;/code&gt; variable by using the approach in the blog post. You can essentially look at this as returning &lt;code&gt;--x&lt;/code&gt; in terms of &lt;code&gt;16px&lt;/code&gt; and return the unitless result. What we have will work, but there’s one small problem. I’ve explicitly set &lt;code&gt;16px&lt;/code&gt; here which is assuming base font size as a fixed pixel amount. Entering &lt;code&gt;1rem&lt;/code&gt; in here doesn’t work (and admittedly I’m not entirely sure why even when updating the &lt;code&gt;@property&lt;/code&gt; defaults).&lt;/p&gt;
&lt;p&gt;What we’ll need is &lt;code&gt;1rem&lt;/code&gt; in terms of pixels. Luckily, &lt;a href=&quot;https://codepen.io/propjockey/pen/WNLLLWy&quot;&gt;Jane has that example&lt;/a&gt; in her post. We’ll use another &lt;code&gt;atan2()&lt;/code&gt; to get the &lt;em&gt;unitless&lt;/em&gt; number of pixels per &lt;code&gt;rem&lt;/code&gt; (&lt;code&gt;16&lt;/code&gt;) and expect to replace the &lt;code&gt;16px&lt;/code&gt; with this result. This actually needs a number with a pixel unit so we can do that quickly by multiplying the unitless result of the new &lt;code&gt;atan2()&lt;/code&gt; by &lt;code&gt;1px&lt;/code&gt; to finally resolve to &lt;code&gt;16px&lt;/code&gt;. Here’s the whole working declaration, followed by the codepen:&lt;/p&gt;
&lt;pre class=&quot;astro-code css-variables&quot; style=&quot;background-color:var(--astro-code-background);color:var(--astro-code-foreground);overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --x {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  syntax: &amp;quot;&amp;lt;length&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: 0px;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;@property&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; --rem {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  syntax: &amp;quot;&amp;lt;length&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;&amp;quot;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-string-expression)&quot;&gt;  initial-value&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;: 0px;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;  inherits: false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;.paragraph&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --rem&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --b&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 25&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** starting size */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --m&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; -30&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** rate of change */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --x&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--b)) / &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--m)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** percent? */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;    --ems&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; tan&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;atan2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--x)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        calc&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;tan&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;atan2&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--rem)&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;))) * 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-comment)&quot;&gt;/** percent! */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    letter-spacing&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt; clamp&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        -0.04&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-function)&quot;&gt;        var&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;(--ems) * 1&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;        0&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-token-keyword)&quot;&gt;em&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-token-constant)&quot;&gt;    )&lt;/span&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--astro-code-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Dynamic letter spacing test&quot; src=&quot;https://codepen.io/fauxserious/embed/ByByMeN?default-tab=css%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen&gt;&lt;p&gt;See the Pen &lt;a href=&quot;https://codepen.io/fauxserious/pen/ByByMeN&quot;&gt;
Dynamic letter spacing test&lt;/a&gt; by Donnie D’Amato (&lt;a href=&quot;https://codepen.io/fauxserious&quot;&gt;@fauxserious&lt;/a&gt;)
on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;&lt;/iframe&gt;
&lt;p&gt;I do wonder if there’s a setup that allows us to remove the second &lt;code&gt;atan2()&lt;/code&gt; usage and insert the &lt;code&gt;1rem&lt;/code&gt; directly here but I haven’t found it yet.&lt;/p&gt;
&lt;p&gt;Whew, what a ride! Maybe one day, we’ll get a supported way of dividing values with units to return a unitless value. But for now, we can wish upon this proof-of-concept. Thanks to Mathieu and Jane for their resources!&lt;/p&gt;</content:encoded><dc:creator>Donnie D&apos;Amato</dc:creator><category>typography</category><category>css</category></item></channel></rss>