<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cathy Lai</title>
    <description>The latest articles on DEV Community by Cathy Lai (@cathylai).</description>
    <link>https://dev.to/cathylai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F904121%2Fdeac3704-41b3-4e1b-a391-0257ed0e729f.jpeg</url>
      <title>DEV Community: Cathy Lai</title>
      <link>https://dev.to/cathylai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cathylai"/>
    <language>en</language>
    <item>
      <title>I Stopped Writing My Resume for Another Software Engineer. That's When Recruiters Started Calling</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Sat, 11 Jul 2026 03:36:09 +0000</pubDate>
      <link>https://dev.to/cathylai/i-stopped-writing-my-resume-for-another-software-engineer-thats-when-recruiters-started-calling-4baj</link>
      <guid>https://dev.to/cathylai/i-stopped-writing-my-resume-for-another-software-engineer-thats-when-recruiters-started-calling-4baj</guid>
      <description>&lt;p&gt;When an international recruiter recently asked for my CV, I instinctively started writing it the way many developers do: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A chronological list of companies, &lt;/li&gt;
&lt;li&gt;Programming languages, frameworks, &lt;/li&gt;
&lt;li&gt;Technical achievements. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it hit me. I wasn't writing this document for a senior engineer. I was writing it for the recruiter sitting between me and the interview. If the first person reading my CV couldn't immediately understand the value I brought, I might never reach the technical interview at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowing the Receivers
&lt;/h2&gt;

&lt;p&gt;So I rewrote it from a different perspective. Instead of simply listing technologies, I described the business context behind my work. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10,000+ emails sent a day (in addition to "Using AWS SES/SQS")&lt;/li&gt;
&lt;li&gt;800+ restaurants / POS everyday (in additional "optimised SQL speed").&lt;/li&gt;
&lt;li&gt;Cut down waste to 1.3% from 10 ~ 15%
&lt;/li&gt;
&lt;li&gt;Critical updates often in 24 hours. &lt;/li&gt;
&lt;li&gt;Increased revenue, reduced costs, improved reliability&lt;/li&gt;
&lt;li&gt;Helped onboarded new clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I still included the languages and frameworks I used, so the CTO can understand, but they became supporting evidence rather than the headline. I also highlighted the moments that demonstrated trust: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delivering critical business updates under tight deadlines, &lt;/li&gt;
&lt;li&gt;Resolving high-priority production issues,&lt;/li&gt;
&lt;li&gt;Taking responsibility for systems the business depended on, and&lt;/li&gt;
&lt;li&gt;Taking initiatives to write a mobile app using my own time. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That small shift completely changed how I viewed a CV. It's not a journal of everything I've done, and it's not a technical specification. Its job is to &lt;strong&gt;communicate your value clearly&lt;/strong&gt; to the person reading it, and that person is often a &lt;strong&gt;recruiter&lt;/strong&gt; before it's ever seen by an engineering manager.&lt;/p&gt;

&lt;p&gt;One lesson I keep coming back to is this: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write for my audience. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Outcome (for now)
&lt;/h2&gt;

&lt;p&gt;After reviewing the rewritten CV, the recruiter was confident enough to forward it to Tata Consultancy Services for a role. Whether or not that particular opportunity works out, it reinforced an important lesson for me: recruiters need to understand your business impact before an engineering manager can appreciate your technical depth. &lt;/p&gt;

&lt;p&gt;Sometimes the biggest improvement isn't gaining another skill—it's learning to communicate the value of the skills you already have.&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>Why Does AI Generate 500 Lines of Code in One Function...? (Part 1/2)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Wed, 24 Jun 2026 04:52:48 +0000</pubDate>
      <link>https://dev.to/cathylai/why-ai-generates-500-lines-of-code-in-one-file-part-12-j6p</link>
      <guid>https://dev.to/cathylai/why-ai-generates-500-lines-of-code-in-one-file-part-12-j6p</guid>
      <description>&lt;p&gt;Summary&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI tends to generate the "path of least resistance" based on what dominates its training data and examples, not necessarily what is architecturally best for a long-lived codebase.&lt;br&gt;
Knowing this, there are steps we can take as seasoned developers to improve the code in multiple passes (part 2). &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  "Amazing" AI Code?
&lt;/h2&gt;

&lt;p&gt;So your non-technical friends have told you about creating a complete landing page in a few minutes! Everything works. They can modify, create, delete elements and AI executed each perfectly. And fast. They love the site, and will definitely create more pages/sites this way.&lt;/p&gt;

&lt;p&gt;You have a look at the code, and it looks like&lt;/p&gt;

&lt;p&gt;❌ Messy code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;XmasMugLandingPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;min-h-screen bg-red-50 text-gray-900 overflow-hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute top-0 left-0 w-full h-full bg-gradient-to-br from-red-200 via-white to-green-200 opacity-70&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative z-10 px-6 py-10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;max-w-7xl mx-auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col lg:flex-row items-center justify-between gap-10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w-full lg:w-1/2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inline-block bg-red-600 text-white px-4 py-2 rounded-full text-sm font-bold mb-6&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;LIMITED&lt;/span&gt; &lt;span class="nx"&gt;CHRISTMAS&lt;/span&gt; &lt;span class="nx"&gt;EDITION&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// ... (messy code all in one function with &lt;/span&gt;
&lt;span class="c1"&gt;//.      utility classes repeated 20 times)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The definition of "good" AI is obviously different from your expectation! ...!?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does AI Generate Code This Way?
&lt;/h2&gt;

&lt;p&gt;When we ask AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build me a landing page with hero, pricing, FAQ and contact form.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI has several competing goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get the answer correct&lt;/li&gt;
&lt;li&gt;Avoid missing dependencies&lt;/li&gt;
&lt;li&gt;Avoid introducing bugs&lt;/li&gt;
&lt;li&gt;Fit within context limits&lt;/li&gt;
&lt;li&gt;Produce something that runs immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not because this is good architecture, But because it maximizes the probability of a correct response.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Has No Long-Term Ownership
&lt;/h3&gt;

&lt;p&gt;A senior engineer thinks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Someone will maintain this for five years.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI's priority:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I need to successfully answer this prompt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are completely different optimization functions.&lt;/p&gt;

&lt;p&gt;This explains: giant components, duplicated utility strings, repeated business logic and copy-pasted sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailwind is easier for AI to generate
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;With CSS Module:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// in a CSS file
.card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem;
}

// in a HTML file
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;{styles.card}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In Tailwind&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;/// all in one file
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-4 p-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously, AI will have less to worry about if it's all in one file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why AI likes shadcn
&lt;/h3&gt;

&lt;p&gt;The AI can see the source code of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;components/ui/button.tsx&lt;/li&gt;
&lt;li&gt;components/ui/card.tsx&lt;/li&gt;
&lt;li&gt;components/ui/dialog.tsx
...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;inside the project. No hidden implementation, and it can modify components directly.&lt;/p&gt;

&lt;p&gt;With a library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the AI cannot edit MUI's source code. It has to understand the MUI API - and that's harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can We Improve On This?
&lt;/h2&gt;

&lt;p&gt;With more and more apps written this way, it seems important that we developers learn to teach AI to tidy and refactor the code! Here are some outlines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate The Page&lt;/li&gt;
&lt;li&gt;Ask AI To Identify Repetition&lt;/li&gt;
&lt;li&gt;Extract Components&lt;/li&gt;
&lt;li&gt;Separate Content From Presentation&lt;/li&gt;
&lt;li&gt;Establish Boundaries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the next article, I will expand on each step and provide a walk through. &lt;/p&gt;

&lt;p&gt;Do you have a similar experience with AI generated code? Does some of the AI tendencies surprise you??&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tailwindcss</category>
      <category>buildinpublic</category>
      <category>css</category>
    </item>
    <item>
      <title>Real World Tailwind CSS: Controlling the Special Cases (Part 2/2)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Sun, 21 Jun 2026 11:56:25 +0000</pubDate>
      <link>https://dev.to/cathylai/real-world-tailwind-css-controlling-the-special-cases-part-22-mdd</link>
      <guid>https://dev.to/cathylai/real-world-tailwind-css-controlling-the-special-cases-part-22-mdd</guid>
      <description>&lt;p&gt;So in the last article, we have discussed the best practices for using Tailwind CSS4 in the real world - which is, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dedicated team maintaining the components &lt;/li&gt;
&lt;li&gt;The other teams just use them for creating pages&lt;/li&gt;
&lt;li&gt;Linter to enforce boundaries, not good will&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this way we "hide" the long utility class strings from rest of the codebase.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Special Landing Page
&lt;/h2&gt;

&lt;p&gt;What happens when marketing launches a new campaign and needs a gradient CTA button that deviates from the core design system??&lt;/p&gt;

&lt;p&gt;The obvious choice might be to do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// The new marketing variant&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ai-campaign&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-gradient-to-r from-emerald-500 to-teal-500 animate-pulse text-lg px-8 py-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks clean today. But in a few years, it might grow to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;
  &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;
  &lt;span class="na"&gt;isMarketing&lt;/span&gt;
  &lt;span class="na"&gt;isAnimated&lt;/span&gt;
  &lt;span class="na"&gt;hasGlowEffect&lt;/span&gt;
  &lt;span class="na"&gt;showConfetti&lt;/span&gt;
  &lt;span class="na"&gt;campaignTheme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"black-friday-2024"&lt;/span&gt;
  &lt;span class="na"&gt;seasonalBadge&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sale"&lt;/span&gt;
  &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"xl"&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's bloated again... this is called &lt;strong&gt;prop explosion&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's the Best Way?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Keep It Local with Tailwind Utilities
&lt;/h3&gt;

&lt;p&gt;Sometimes a design is so specific that it's hard to imagine anyone using it again. So, just make the page live in a separate directory and use utility classes directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;////  Inside @/app/marketing/campaign/page.tsx ////&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CampaignPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;Next&lt;/span&gt; &lt;span class="nx"&gt;Generation&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;AI&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Custom, one-off visual classes are localized entirely to this page */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;cn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-indigo-600 text-white px-4 py-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;Get&lt;/span&gt; &lt;span class="nx"&gt;Early&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the campaign ends, just delete the page. The  component is left untouched.&lt;/p&gt;


&lt;h3&gt;
  
  
  Option 2: Add a Variant
&lt;/h3&gt;

&lt;p&gt;If there are only a handful of visual styles and they genuinely represent something the business uses repeatedly, I'd probably just add another variant.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;voucher&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The props felt easy enough to understand, and it keeps the page code nice and clean. In a smaller codebase, maintaining a few extra variants would not be a big deal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3: Create a Specialized Component
&lt;/h3&gt;

&lt;p&gt;There's also a middle ground. If I find myself copying the same styling around a campaign or a particular section of the application, I'd probably start wondering whether it's worth creating something like a .&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CampaignButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;cn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-gradient-to-r ...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;className&lt;/span&gt;
      &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Base component&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Testimonials&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// Campaign-specific component&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CampaignButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Get&lt;/span&gt; &lt;span class="nx"&gt;Early&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/CampaignButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;That way the shared Button stays focused on being a button, while the campaign gets its own reusable abstraction. It feels like a reasonable compromise when you're not quite ready to promote something into the design system, but you don't want duplicated styling everywhere either.&lt;/p&gt;

&lt;p&gt;The goal is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep shared components focused, understandable, and maintainable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Do you agree with the tradeoffs? What are your tips for writing even tidier and more maintainable components? Please share your experience ~ :)&lt;/p&gt;



</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>frontend</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Real World Tailwind CSS: The "Gatekeeper" Architecture: A Senior Developer's Guide (Part 1/2)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Fri, 19 Jun 2026 09:25:12 +0000</pubDate>
      <link>https://dev.to/cathylai/real-world-tailwind-css-the-gatekeeper-architecture-a-senior-developers-guide-part-12-m36</link>
      <guid>https://dev.to/cathylai/real-world-tailwind-css-the-gatekeeper-architecture-a-senior-developers-guide-part-12-m36</guid>
      <description>&lt;p&gt;So, armed with the knowledge of Tailwind and what it has to offer, how should we actually start our next large-scale project? To answer that question, it helps to look at how mature design-system teams structure frontend development in large organizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Blueprint: Component Authors vs. Feature Developers
&lt;/h2&gt;

&lt;p&gt;After a quick research, I've found that many successful design systems, including examples such as Shopify Polaris, are built around a simple idea: not everyone on the team is responsible for styling decisions.&lt;/p&gt;

&lt;p&gt;Instead, styling responsibilities are often separated into two distinct roles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Component Authors (The Gatekeepers):&lt;/strong&gt; This core team builds foundational UI primitives. They write the Tailwind classes, manage accessibility, dark mode, component states, and design-token compliance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Feature Developers (The Assemblers):&lt;/strong&gt; This team builds product features, workflows, and dashboards. Their primary focus is business logic and page composition rather than choosing colors, typography, or spacing scales.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Many organizations reinforce this boundary through a combination of lint rules, code review standards, internal tooling, and shared team conventions.&lt;/p&gt;

&lt;p&gt;✔ Allowed: layout and composition utilities (&lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;grid&lt;/code&gt;, &lt;code&gt;gap-4&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;❌ Discouraged: introducing new visual styles such as &lt;code&gt;bg-indigo-600&lt;/code&gt; or &lt;code&gt;text-lg&lt;/code&gt; directly inside feature pages when an approved component already exists&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Example: The Standard Corporate Button
&lt;/h2&gt;

&lt;p&gt;To see this boundary in practice, let's look at how a standard corporate button might be built and consumed.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. What the Component Author Builds
&lt;/h3&gt;

&lt;p&gt;The Component Authors create a reusable primitive inside &lt;code&gt;@/components/ui/button.tsx&lt;/code&gt;. The visual styling is centralized behind a clean TypeScript API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Managed by the Design System Team (@/components/ui/button.tsx)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonHTMLAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Intended for layout adjustments when needed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;px-4 py-2 rounded-lg font-medium text-sm transition-all focus:outline-none focus:ring-2 focus:ring-offset-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-slate-100 text-slate-700 hover:bg-slate-200 focus:ring-slate-500&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseStyles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. How the Feature Developer Uses It
&lt;/h3&gt;

&lt;p&gt;When a Feature Developer builds a dashboard page, they primarily assemble existing building blocks and focus on feature logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside @/app/dashboard/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/ui/button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      ...

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mt-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Save Changes
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The visual design remains centralized in the component, while the page controls placement and composition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Deleting a Button
&lt;/h3&gt;

&lt;p&gt;The developer who originally created a page has moved on, and another developer needs to remove a button.&lt;/p&gt;

&lt;p&gt;Because the visual styling is already encapsulated inside a shared component, there is no need to search through multiple CSS files looking for button-specific styles. The page simply stops using the component.&lt;/p&gt;

&lt;p&gt;This reduces the risk of obsolete styling logic lingering in the codebase and makes long-term maintenance easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Colour or Theme Change
&lt;/h3&gt;

&lt;p&gt;Suppose the company decides to move from Indigo to Violet as its primary brand color.&lt;/p&gt;

&lt;p&gt;In many design-system architectures, the update happens in a single shared location (or design-token layer). The change then propagates consistently across the application without requiring developers to update individual pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Bigger Lesson
&lt;/h2&gt;

&lt;p&gt;The important idea is not Tailwind itself. The key architectural concept is the boundary between &lt;strong&gt;component authors&lt;/strong&gt; and &lt;strong&gt;feature developers&lt;/strong&gt;. Tailwind is simply one effective tool for implementing that separation while keeping styles close to the components that own them.&lt;/p&gt;

&lt;h2&gt;
  
  
  In the Next Article...
&lt;/h2&gt;

&lt;p&gt;This approach covers most day-to-day product development. But what happens when the marketing team wants a one-off campaign page with a glowing, animated gradient button that doesn't fit the design system? Do we extend the component library? Create a special exception? Relax the rules?&lt;/p&gt;

&lt;p&gt;In Part 2, we'll explore practical ways to handle architectural exceptions without turning the codebase into a free-for-all.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>buildinpublic</category>
      <category>css</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Tailwind CSS4: Why Those Inline Styles Are Actually More Scalable - A Senior CSS Developer's Guide</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Tue, 16 Jun 2026 05:09:55 +0000</pubDate>
      <link>https://dev.to/cathylai/tailwind-css4-why-those-inline-styles-are-actually-more-scalable-a-senior-css-developers-guide-hdj</link>
      <guid>https://dev.to/cathylai/tailwind-css4-why-those-inline-styles-are-actually-more-scalable-a-senior-css-developers-guide-hdj</guid>
      <description>&lt;p&gt;So you have heard about the Tailwind CSS and want to incorporate into your new project. But those inline styles look so polluted - they look similar to the old "style="font-size: 12px; color: green; font-style: italics" stuff we are told to avoid. What is going on here? Are we going back??&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Plain CSS Classes
&lt;/h2&gt;

&lt;p&gt;Before you run the other way, I think we have all encounter this problem maintaining a large codebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Developer A wrote this 2 years ago */&lt;/span&gt;
&lt;span class="nc"&gt;.card-variant-3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f3f4f6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After 2 years, the stylesheet is 5,000 lines long. And this class could be used anywhere in the codebase of millions of lines. &lt;/p&gt;

&lt;p&gt;Developer B is too scared to modify .card-variant-3 because they don't know if it's used on some obscure marketing page... So they just append a new class to the bottom of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card-variant-3-updated&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f3f4f6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* &amp;lt;--- Only changed the padding! */&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;: Bundle sizes grow linearly with the app. You have to spend your time naming things (.main-content-inner, .card-body-flex) and jumping between HTML and CSS files.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tailwind Gives Us
&lt;/h2&gt;

&lt;p&gt;Tailwind solves this problem by creating "utility classes" such as  "bg-mint-500", "text-mint-500", and "border-mint-500" from CSS variables which one can use in the HTML like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-mint-500"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; Introduction &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This restructure gives us two advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Reduced CSS Bundle Size&lt;/strong&gt;: Because you reuse bg-mint-500 or p-4 over and over, you stop writing new CSS lines. Whether you have 10 pages or 10,000 pages, your production CSS stays virtually the same size (often a fraction of traditional stylesheets).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dramatically Lower Risk in Refactoring&lt;/strong&gt;: Everything that makes this element look the way it does lives &lt;strong&gt;exclusively&lt;/strong&gt; inside this component block. If you delete the component, the styles are deleted instantly alongside it. There is absolutely zero risk of breaking a sidebar on the other side of your application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What about Readability??
&lt;/h2&gt;

&lt;p&gt;The biggest reason this is okay is that you only have to look at it occasionally.&lt;/p&gt;

&lt;p&gt;In modern web development, we rarely write raw, static HTML pages anymore. We use component-based architectures (React, Vue, Svelte, Blade, Astro, etc.).&lt;/p&gt;

&lt;p&gt;When you style an elements block like this, you immediately &lt;strong&gt;encapsulate&lt;/strong&gt; it inside a reusable component. E.g.,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Card&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"premium"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that component is saved, the "ugly" implementation details disappear behind a clean, semantic custom tag. Your main layout code remains pristine, while the styling mess is safely quarantined inside a single file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We traded pretty markup for bulletproof maintainability. We allowed our HTML to look messy in development so that our production architecture could remain perfectly clean, predictable, and isolated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;I will discuss more about Tailwind CSS in real projects. Follow me for more such articles!&lt;/p&gt;

&lt;p&gt;Please comment below - how do you manage your CSS and do you think utility classes is a good idea??&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>What Are Customers Already Asking For That I'm Ignoring?</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Sat, 13 Jun 2026 10:46:08 +0000</pubDate>
      <link>https://dev.to/cathylai/what-are-customers-already-asking-for-that-im-ignoring-2d59</link>
      <guid>https://dev.to/cathylai/what-are-customers-already-asking-for-that-im-ignoring-2d59</guid>
      <description>&lt;h2&gt;
  
  
  Another Cool Tech to Add?
&lt;/h2&gt;

&lt;p&gt;After playing with some AI-generated garden mockups, I thought it'd be cool to let users choose the styles they want, eg. Scandinavian, Mediterranean, or Japanese gardens... It is technically interesting. I could imagine a dropdown, a scrollable style gallery? And I can tweak the prompts to make sure AI generate a good suggestion...&lt;/p&gt;

&lt;p&gt;But before I went on a tangent, which could be weeks down the track, I remembered &lt;em&gt;&lt;a href="https://www.momtestbook.com/" rel="noopener noreferrer"&gt;The Mum Test&lt;/a&gt;&lt;/em&gt; book I've read. So I've decided to do some research on the users online and what they actually &lt;em&gt;already&lt;/em&gt; doing? &lt;/p&gt;

&lt;h2&gt;
  
  
  Observation 1: Successful YT Landscape Channels
&lt;/h2&gt;

&lt;p&gt;Many of the most popular garden-related videos were not focused on luxury landscaping or designer gardens. Instead, they focused on practical topics:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrv0mr0emzt7xd32jwx1.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrv0mr0emzt7xd32jwx1.jpeg" alt=" " width="800" height="1151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A recurring theme was that viewers seemed to appreciate realistic improvements rather than perfection.&lt;/p&gt;

&lt;p&gt;The message was often:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You don't need a $20,000 renovation. Here are a few simple changes that make a noticeable difference."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This suggests that many homeowners are looking for confidence and direction rather than professional-level landscape design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observation 2: Reddit Questions
&lt;/h2&gt;

&lt;p&gt;Many posts were not asking for specific garden styles. Instead, they looked more like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb77mt4fukyhqakvwdkvg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb77mt4fukyhqakvwdkvg.jpeg" alt=" " width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rkmk22vph7thykji6bx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rkmk22vph7thykji6bx.png" alt=" " width="800" height="459"&gt;&lt;/a&gt;&lt;br&gt;
Many homeowners appeared overwhelmed by the number of possible choices.&lt;/p&gt;

&lt;p&gt;In some cases, they could not clearly identify what was wrong with the garden. They simply knew that it didn't feel right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working Hypothesis
&lt;/h2&gt;

&lt;p&gt;Based on these observations, my current hypothesis is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Homeowners may not be looking for perfect gardens.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead, they may be looking for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reassurance&lt;/li&gt;
&lt;li&gt;Direction&lt;/li&gt;
&lt;li&gt;Confidence&lt;/li&gt;
&lt;li&gt;Visualization of realistic outcomes&lt;/li&gt;
&lt;li&gt;Affordable improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, the problem may not be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Design me a Japanese garden."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem may actually be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me what this could look like if I spent a few hundred dollars and a weekend improving it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Proposed Experiment
&lt;/h2&gt;

&lt;p&gt;What is the cheapest and the easiest way for me to test this theory? Probably not &lt;/p&gt;

&lt;p&gt;❌ Building more tools on my website, &lt;br&gt;
❌ Nor is it creating more static pages.&lt;/p&gt;

&lt;p&gt;I just need to reply to users who are actively anticipating answers. Instead of promoting a product or linking to a website, I will try to be genuinely helpful by:&lt;/p&gt;

&lt;p&gt;✔️ Answering their specific question&lt;br&gt;
✔️ Suggesting a few practical improvements&lt;br&gt;
✔️ Attaching relevant before-and-after visual examples&lt;br&gt;
✔️ Showing realistic transformations&lt;/p&gt;

&lt;p&gt;The goal is not to sell anything. It is to observe whether visual examples help people understand and evaluate potential improvements more easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Success Criteria
&lt;/h2&gt;

&lt;p&gt;Some signals that would support the hypothesis include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Positive replies and engagement&lt;/li&gt;
&lt;li&gt;Follow-up questions&lt;/li&gt;
&lt;li&gt;Users saying the images helped them visualize possibilities&lt;/li&gt;
&lt;li&gt;Requests for similar examples&lt;/li&gt;
&lt;li&gt;Discussions shifting from confusion to specific decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conversely, if people ignore the images or focus on entirely different concerns, that would provide valuable feedback as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Would I Keep Building My AI Tools?
&lt;/h2&gt;

&lt;p&gt;Yes, I definitely will keep building and learning. But I now am more clear that those are for my own learning, not necessary for the customers. This will keep me from feeling discouraged when there are no user signups.&lt;/p&gt;

</description>
      <category>ux</category>
      <category>devjournal</category>
      <category>buildinpublic</category>
      <category>ai</category>
    </item>
    <item>
      <title>So Nobody Told Me that Prompting Is Not Programming...</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Wed, 10 Jun 2026 06:38:36 +0000</pubDate>
      <link>https://dev.to/cathylai/why-my-ai-garden-app-kept-generating-hotels-and-how-i-fixed-it-4ok4</link>
      <guid>https://dev.to/cathylai/why-my-ai-garden-app-kept-generating-hotels-and-how-i-fixed-it-4ok4</guid>
      <description>&lt;p&gt;Now I've got the GUI of the app, it's time to "program" the prompt! Without much knowledge, I treated the prompt exactly like an API call. I uploaded a photo of a messy yard, and start typing the prompt.&lt;/p&gt;

&lt;p&gt;The Sample Before Image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xeqeo8a3rl5afnnldqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xeqeo8a3rl5afnnldqi.png" alt="Messy garden" width="496" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a set of instructions (add lawns, furniture, mulch, native evergreen shrubs, furniture, and solar and string lights), I have got this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n5hw0aumznw16zzpjq9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n5hw0aumznw16zzpjq9.png" alt="Improved but washed out" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks okay, but the texture feels washed out and uninviting... I have tried several other examples, and they all had the same problem. No matter how many times I asked it to add vibrant lawns, nice lighting, it gave me worn out lawns, washed out feeling, grey sky, and may or may not include lighting...&lt;/p&gt;

&lt;h2&gt;
  
  
  How About Just Describe the "Mood"??
&lt;/h2&gt;

&lt;p&gt;So I asked GPT how to solve this problem? It told me that I should just describe the "mood", eg. homely, motel-like, morning sun, neat, tidy, simple, and elegant. And let it arrange the objects. So I've got something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyh66nuwp9epv0ytud2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvyh66nuwp9epv0ytud2f.png" alt="Hotel like" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks better... however it is too much like a neat hotel, a concrete jungle, and the materials look expensive with a lot of construction work. I keep asking it to be "homely", "affordable"; but it seems to ignore those.   &lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Meaning of Prompt
&lt;/h2&gt;

&lt;p&gt;So I started from scratch, using another model (Gemini) and describe the project and the problems I encountered.&lt;/p&gt;

&lt;p&gt;Gemini told me something surprising...! That is &lt;strong&gt;Prompt engineering is not programming. I had my mental model wrong all along.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Probabilistic Compiler vs. Latent Space
&lt;/h2&gt;

&lt;p&gt;When I write traditional code, I am issuing commands to a deterministic CPU. When I prompt, I am interacting with a &lt;strong&gt;statistical weight matrix&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The AI doesn't compile my text instructions; it navigates a massive, multi-dimensional mathematical map of human concepts called &lt;strong&gt;latent space&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every word, phrase, and visual style in the model's training data lives on this map as a set of coordinates. Concepts that are similar sit in the same geographic neighborhood.&lt;/p&gt;

&lt;p&gt;My prompt isn't an imperative command—it is a &lt;strong&gt;vector coordinate&lt;/strong&gt; pulling the generator toward a specific statistical zip code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Taxi Analogy:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Traditional coding&lt;/strong&gt; is giving a driver exact GPS coordinates: &lt;em&gt;"Drive 400 meters, turn left, park at Spot #42."&lt;/em&gt; The outcome is identical every time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompting&lt;/strong&gt; is calling a cab and saying: &lt;em&gt;"Take me somewhere cozy in the Arts District."&lt;/em&gt; Because the neighborhood is huge, the driver drops me off at a completely different alleyway every single time. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both spots technically fit my description, but the visual layout changes entirely. If I leave my prompt loose, the model wanders around that neighborhood, causing the massive variance I see.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Solution: Structuring the "Config File"
&lt;/h2&gt;

&lt;p&gt;The fix for me was to pass the AI a clean, structured pseudo-schema rather than a paragraph of text. By defining strict parameters, I forced spatial and material continuity.&lt;/p&gt;

&lt;p&gt;Here is the structured approach that finally tamed the variance for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Environment Map]: Use the uploaded backyard photo strictly for spatial boundary context.
[Style Const]: Bright morning sunlight, high-contrast architectural photography, homely vibe.
[Financial Guardrails]: Show only low-cost, DIY-friendly materials. 
  - ALLOW: Dark bark mulch, timber edging, river gravel.
  - EXCLUDE: Masonry, expensive stone pavers, structural retaining walls.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why this works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Locks the Environment:&lt;/strong&gt; Defining the lighting and camera angle upfront eliminates the AI's tendency to randomly shift from midday sun to moody twilight.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardcodes the Constraints:&lt;/strong&gt; Instead of letting the AI guess what "low budget" means, I explicitly whitelist cheap materials (mulch, gravel) and blacklist expensive ones (paving, walls).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By shifting my mindset from "solving a spatial puzzle with code" to "setting strict boundary parameters on a statistical matrix," I stopped fighting the randomness and started leveraging the inspiration.&lt;/p&gt;

&lt;p&gt;The Result &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjruxut4owuxftyaa8wby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjruxut4owuxftyaa8wby.png" alt="Correct result" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is much closer to the homeliness I wanted, with inspiring lighting, vibrant lawns, low maintenance shrubs, and achievable garden beds.&lt;/p&gt;

</description>
      <category>promptengineering</category>
      <category>ai</category>
      <category>devjournal</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>How Suddenly ChatGPT Understood Me - by Me Changing One Word</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Fri, 05 Jun 2026 01:16:41 +0000</pubDate>
      <link>https://dev.to/cathylai/i-changed-one-word-in-my-prompt-and-added-an-image-and-suddenly-chatgpt-understood-me-17e9</link>
      <guid>https://dev.to/cathylai/i-changed-one-word-in-my-prompt-and-added-an-image-and-suddenly-chatgpt-understood-me-17e9</guid>
      <description>&lt;p&gt;This morning I was trying to generate a simple accessibility modification for a backyard deck.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffb7jf6s4imq4o5d7ntb5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffb7jf6s4imq4o5d7ntb5.jpeg" alt=" " width="720" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted something like a small gentle ramp so an elderly person in a wheelchair could get onto the deck more easily.&lt;/p&gt;

&lt;p&gt;Instead, I kept getting images like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0k7jnlxm5q3kc6fur3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0k7jnlxm5q3kc6fur3e.png" alt=" " width="760" height="1350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ramp is technically correct, but completely wrong for the homeowner!&lt;/p&gt;

&lt;h2&gt;
  
  
  My First Assumption: The Model Isn’t Good Enough
&lt;/h2&gt;

&lt;p&gt;After about 10+ trials, I concluded that ChatGPT is just not good enough. Perhaps I should find a better model that's especially designed for image generation?&lt;/p&gt;

&lt;p&gt;After sleeping on it for a day or two, I have decided to "think like AI". If it has been trained by ramp images, what would general ones look like?&lt;/p&gt;

&lt;p&gt;Even though the incorrect images are all different, they all had the same underlying idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;long pathway&lt;/li&gt;
&lt;li&gt;extensive handrails&lt;/li&gt;
&lt;li&gt;institutional appearance&lt;/li&gt;
&lt;li&gt;accessibility-first design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI wasn’t being random. It was being remarkably consistent. That meant the problem might not be the model at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigating the Word “Ramp”
&lt;/h2&gt;

&lt;p&gt;I searched Google Images. Most results looked surprisingly similar to the AI outputs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9det6tqo1s7agdnffxhd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9det6tqo1s7agdnffxhd.jpeg" alt=" " width="799" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The common theme was: long, extended slop, large handrails. It spells safety and accessibility, not residential elegance.&lt;/p&gt;

&lt;p&gt;At that point I started wondering: if image models learn patterns from large collections of internet images, perhaps “wheelchair ramp” strongly correlates with these kinds of designs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing the Prompt
&lt;/h2&gt;

&lt;p&gt;So I made two changes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, I removed the word: &lt;em&gt;ramp&lt;/em&gt; and replaced it with: &lt;em&gt;slope&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to remove the strong association with commercial accessibility infrastructure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Second, I supplied a reference image showing the kind of small timber transition I actually wanted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv9haubf8mm4ql35b3eg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv9haubf8mm4ql35b3eg.jpeg" alt=" " width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generated design became much closer to my intent! Not because the model suddenly became smarter. Because the instructions became clearer. The AI no longer had to guess what kind of ramp I meant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj25ipxtkozsmypqur87w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj25ipxtkozsmypqur87w.png" alt=" " width="800" height="1421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final result:
&lt;/h2&gt;

&lt;p&gt;And then I asked it to match the colour of the deck and put the handrail on the other side:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksky6fclv4sf7psfvcgx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksky6fclv4sf7psfvcgx.png" alt=" " width="800" height="1421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it’s perfect:)&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;The interesting lesson wasn’t about landscaping. It was about how AI systems interpret language.&lt;/p&gt;

&lt;p&gt;When I used the word “ramp”, the model wasn’t hearing “elegant backyard modification”. It was hearing: “the average concept of a wheelchair ramp found across millions of images”. Once I understood that, the solution became obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choose words carefully&lt;/li&gt;
&lt;li&gt;look for patterns in failures&lt;/li&gt;
&lt;li&gt;provide examples when possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I almost dismissed GPT and tried to signed up for a more expensive app. Instead, I solved it by understanding how the model was likely interpreting my prompt. Sometimes the next step isn’t a better model. Sometimes it’s spending ten minutes asking:&lt;/p&gt;

&lt;p&gt;“What does this word mean to the AI?”&lt;/p&gt;

</description>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>devjournal</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>My Potential Users are Just Busy… Or Are They?</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Wed, 03 Jun 2026 14:33:01 +0000</pubDate>
      <link>https://dev.to/cathylai/my-potential-users-are-just-busy-or-are-they-4olf</link>
      <guid>https://dev.to/cathylai/my-potential-users-are-just-busy-or-are-they-4olf</guid>
      <description>&lt;p&gt;When I first told a friend about my AI garden visualization idea, she said&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What a great idea! Sounds interesting!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I asked her to send me her backyard photos for testing! Then a week went by, she didn't send any photo. I assume: "she’s a busy doctor. She works long shifts, late nights, and weekends." &lt;/p&gt;

&lt;p&gt;But after reading &lt;em&gt;The Mom Test&lt;/em&gt;, I’m starting to think there was a simpler explanation: &lt;strong&gt;The problem wasn’t painful enough.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  "Can You Help"?
&lt;/h2&gt;

&lt;p&gt;About a week later, she contacted me again. This time, she sent photos of her parents’ backyard.&lt;/p&gt;

&lt;p&gt;Not just the photos - she explained the layout, described the constraints, and talked about wheelchair access for her elderly parents.&lt;/p&gt;

&lt;p&gt;I then did a AI mockup for it. She reviewed it, thanked me and asked for more changes. And, she mentioned something I never asked about: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A landscape designer had quoted approximately $3,600 just for the design work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Actions vs. Words
&lt;/h2&gt;

&lt;p&gt;One of the biggest lessons from &lt;em&gt;The Mom Test&lt;/em&gt; is that people are poor predictors of their future behavior. What they &lt;em&gt;do&lt;/em&gt; is always more informative than what they &lt;em&gt;say&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Looking at my friend’s actions, the data points were clear:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Know:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;She has a real problem and is actively trying to solve it.&lt;/li&gt;
&lt;li&gt;She is willing to spend money to solve it but $3,600 is too much.&lt;/li&gt;
&lt;li&gt;She wanted to see what AI could do without the designer. &lt;/li&gt;
&lt;li&gt;She is interested enough to come back with more requests. &lt;/li&gt;
&lt;li&gt;The mockup made it clear for possible outcome, and provided ideas for additional improvements and other considerations. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I Don’t Know:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether people like her would pay for an AI visualization app. There is no validation from her actions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A More Defensible Hypothesis
&lt;/h2&gt;

&lt;p&gt;Based on the evidence, a more reasonable hypothesis might be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Homeowners planning significant outdoor renovations want a low-cost way to visualize ideas before committing to expensive design and construction decisions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s not necessarily:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;People want an AI garden design app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first statement comes from observed behavior. The second statement is still an &lt;strong&gt;assumption&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Art of Staying Detached
&lt;/h2&gt;

&lt;p&gt;To actually see these insights, I'm realizing the most important thing is to approach these conversations as a blank slate.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have to remain completely detached from the outcome.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without that detached attitude, it is still incredibly easy to be biased and read into a situation something that isn't actually there. It requires a lot of practice and a constant shifting of perspective.&lt;/p&gt;

&lt;p&gt;I am still learning. But with practice, I know I can get better at it over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Biggest Takeaway
&lt;/h2&gt;

&lt;p&gt;The most valuable thing I gained from this experience wasn’t validation for my product. It was learning to &lt;strong&gt;watch actions&lt;/strong&gt; instead of listening only to words.&lt;/p&gt;

&lt;p&gt;When someone says &lt;em&gt;"Interesting idea,"&lt;/em&gt; it tells me very little. But when someone spends money, sends photos, reviews mockups, and keeps coming back - that's evidence that the problem is serious enough to even began the conversations. &lt;/p&gt;

&lt;p&gt;Because people don’t make time for what sounds interesting.. They make time for what hurts&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>buildinpublic</category>
      <category>uxdesign</category>
      <category>ux</category>
    </item>
    <item>
      <title>My Biggest Fear In Talking to Users – What If They Just Want a Static Website? (Part 1/2)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Tue, 02 Jun 2026 06:07:31 +0000</pubDate>
      <link>https://dev.to/cathylai/my-biggest-fear-in-talking-to-users-what-if-they-just-want-a-static-website-part-12-507d</link>
      <guid>https://dev.to/cathylai/my-biggest-fear-in-talking-to-users-what-if-they-just-want-a-static-website-part-12-507d</guid>
      <description>&lt;p&gt;&lt;em&gt;Reading Notes From the Book "The Mum Test"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that I've build a clever AI image generation app, everybody will just jump on the chance to try it... Right?&lt;/p&gt;

&lt;p&gt;As a developer, that's what I'd like to think.  &lt;/p&gt;

&lt;p&gt;I imagine sophisticated architectures, AI integrations, user accounts, databases, cloud infrastructure, clever AI image generation pipelines, system prompt and context engineering, and all the exciting technology that will solve users' problems. &lt;/p&gt;

&lt;h2&gt;
  
  
  Is that What the Users Want?
&lt;/h2&gt;

&lt;p&gt;Maybe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They just want to browse a collection of garden makeover photos?&lt;/li&gt;
&lt;li&gt;They don't want to upload their own property photos? For privacy concerns?&lt;/li&gt;
&lt;li&gt;They don't want to wait 60 seconds for an AI-generated image?&lt;/li&gt;
&lt;li&gt;A simple gallery of before-and-after examples solves 90% of their problems?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a developer, that's a surprisingly difficult thought to accept. After all, generating AI garden transformations sounds much more exciting than displaying a set of static images. &lt;/p&gt;

&lt;p&gt;And that's exactly why it makes me uncomfortable. Because if a simple solution provides more value to users than a complicated one, then I've spent a lot of time solving the &lt;strong&gt;wrong problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A boring solution with real users is infinitely more valuable than a clever solution with &lt;strong&gt;none&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Book "The Mum Test"
&lt;/h2&gt;

&lt;p&gt;That's one reason I've started reading &lt;em&gt;The Mom Test&lt;/em&gt; by Rob Fitzpatrick before launching my site.&lt;/p&gt;

&lt;p&gt;The book challenges a mistake many founders make:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Asking people what they think of an idea instead of understanding what they already do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here are my notes in the key points:&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway #1: Talk About Their Behaviour, Not Your Product
&lt;/h2&gt;

&lt;p&gt;A homeowner might tell me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"An AI garden design tool sounds useful."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But that's not the question that matters.&lt;/p&gt;

&lt;p&gt;A better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What did you do the last time you wanted ideas for your garden?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe they browsed Instagram? Maybe they asked a question in a Facebook or Reddit gardening group. Or just ask their parents? Friends? Do they hire a landscape designer instead. What about gardening magazines? Or most people just buy plants without planning? Because they look good?&lt;/p&gt;

&lt;p&gt;If that's how they currently solve the problem, then that's where I should start paying attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway #2: Past Actions Are More Valuable Than Future Promises
&lt;/h2&gt;

&lt;p&gt;People often &lt;strong&gt;overestimate&lt;/strong&gt; what they will do in the future. The Mom Test argues that future promises are weak evidence. Past behaviour is much stronger.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Would you use an AI visualizer?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I should be asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How many hours did you spend looking for garden ideas last month?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Have you ever paid for landscaping advice?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those answers reveal whether the problem is important enough for people to spend time or money solving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway #3: Look for Actions, Not Compliments
&lt;/h2&gt;

&lt;p&gt;As developers, we love positive feedback. But compliments can be misleading. The strongest signals are &lt;strong&gt;actions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do they reach out for advice? &lt;/li&gt;
&lt;li&gt;Do they checkout gardening materials? &lt;/li&gt;
&lt;li&gt;Do people share photos? And &lt;em&gt;like&lt;/em&gt; makeover images? &lt;/li&gt;
&lt;li&gt;Do they bookmark examples?&lt;/li&gt;
&lt;li&gt;Do they trust the landscape/garden advice?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These behaviours tell a much clearer story than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"That's a great idea."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Action Points for Me
&lt;/h2&gt;

&lt;p&gt;Having these 3 takeaways in mind,  I will start listing out the practical actions to take to understand the users more. I will share them in the next part of this series!&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>buildinpublic</category>
      <category>uidesign</category>
      <category>ai</category>
    </item>
    <item>
      <title>Spending Hours Designing the UI? Or Just Telling AI the Pain Story</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Sun, 31 May 2026 05:21:45 +0000</pubDate>
      <link>https://dev.to/cathylai/spending-hours-designing-the-ui-or-just-telling-ai-the-pain-story-58al</link>
      <guid>https://dev.to/cathylai/spending-hours-designing-the-ui-or-just-telling-ai-the-pain-story-58al</guid>
      <description>&lt;p&gt;Now that I get my &lt;a href="https://dev.to/cathylai/garden-visualizer-would-results-improve-using-example-images-screenshots-included-jn2"&gt;backend API services &lt;/a&gt;in place, it's time to design a frontend! Just describe the mood to AI, right? I did, and it looked... fine. But it didn't feel right. It didn't match the soul of why I was building it.&lt;/p&gt;

&lt;p&gt;So, instead of looking at font pairing blogs for three hours, I sat down and told Gemini the personal story and frustration behind the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outdated Garden Styles
&lt;/h2&gt;

&lt;p&gt;I explained that as a busy professional, I felt completely alienated by the gardening world. Every time I looked up gardening advice on YouTube or blogs to fix up my yard, I was met with slow acoustic country music, rambling lectures full of jargons, and an aesthetic that felt like a grandmother giving advice.&lt;/p&gt;

&lt;p&gt;It felt old fashioned, inefficient, and totally out of touch with how my peers and I live. For example:&lt;/p&gt;

&lt;p&gt;❌ Old fashion garden style&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j9r6z77u8lin9pcs0js.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j9r6z77u8lin9pcs0js.jpg" alt="Cottage Garden impossible to maintain" width="800" height="1050"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don't want a chaotic cottagecore wildflower patch that takes six hours a week to prune. Our design references are the sleek tech offices we work in, the boutique hotels we visit, or the trendy cafes with clean gravel, square lawns, structural plants, and warm outdoor lighting. We just want a beautiful, low-maintenance outdoor lounge where we can turn on some music, host a weekend barbecue, and have drinks with friends on a summer evening.&lt;/p&gt;

&lt;p&gt;&lt;span&gt;✔&lt;/span&gt; Modern outdoor courtyard &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3fi2qi9rkhdu018bavw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3fi2qi9rkhdu018bavw.jpg" alt="Modern garden" width="800" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I told Gemini: “I built a tool that takes a messy yard and instantly visualizes that clean, hotel-grade hosting space. Help me pick a font system that talks to these busy professionals, not their grandmothers.”&lt;/p&gt;

&lt;h2&gt;
  
  
  10 Seconds to the Right Font...!
&lt;/h2&gt;

&lt;p&gt;Main title font "Fraunces"&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nst6y2azx6w5pfhi7mj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nst6y2azx6w5pfhi7mj.png" alt="Font styles " width="800" height="898"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Subtitle and body text "Inter"&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3aghe0vooew48d2wl3ut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3aghe0vooew48d2wl3ut.png" alt="Font styles " width="800" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind Changes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Load Fraunces from Fontshare in global.css
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url("https://api.fontshare.com/v2/css?f[]=fraunces@500,600,700&amp;amp;display=swap")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Register a Tailwind font family in global.css
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;--font-inter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--font-inter-family&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nt"&gt;ui-sans-serif&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;system-ui&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;sans-serif&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;--font-fraunces&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Fraunces"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;ui-serif&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;Georgia&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;serif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Apply it on the page title (page.tsx)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font-fraunces text-5xl leading-[0.95] font-semibold tracking-tight text-[#1f321d] sm:text-6xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nx"&gt;GardenViz&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font-inter mt-2 text-lg font-medium tracking-tight text-[#3a4f35] sm:text-xl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nx"&gt;Garden&lt;/span&gt; &lt;span class="nx"&gt;Design&lt;/span&gt; &lt;span class="nx"&gt;Visualizer&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;Busy&lt;/span&gt; &lt;span class="nx"&gt;Homeowners&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30dev155rks3zpq0ylpq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30dev155rks3zpq0ylpq.png" alt="App Title and font" width="780" height="199"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcutd4c7nc2ijjb3455wa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcutd4c7nc2ijjb3455wa.png" alt="App screenshot with the new font" width="799" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think it looks quite good! :)&lt;/p&gt;

&lt;h2&gt;
  
  
  AI as a Collaborator
&lt;/h2&gt;

&lt;p&gt;By treating the AI as a creative collaborator and feeding it the context of a real human frustration rather than just a technical prompt, I got a design system that feels incredibly intentional.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>uidesign</category>
      <category>devjournal</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>OpenAI Image Generation: Would Results Improve with Positive&amp; Negative Example Images? (Screenshots Included)</title>
      <dc:creator>Cathy Lai</dc:creator>
      <pubDate>Thu, 28 May 2026 04:17:38 +0000</pubDate>
      <link>https://dev.to/cathylai/garden-visualizer-would-results-improve-using-example-images-screenshots-included-jn2</link>
      <guid>https://dev.to/cathylai/garden-visualizer-would-results-improve-using-example-images-screenshots-included-jn2</guid>
      <description>&lt;h2&gt;
  
  
  Perfect Prompts Needed?
&lt;/h2&gt;

&lt;p&gt;In creating a &lt;a href="https://dev.to/cathylai/how-i-stopped-despairing-over-the-backyard-mess-and-started-an-ai-side-project-3f9a"&gt;garden visualizer&lt;/a&gt; to help home owners with their landscape decisions, I assumed that I needed to "program" the text prompt perfectly in order to give good results. So spending time tweaking the text descriptions seems natural. &lt;/p&gt;

&lt;p&gt;However, after a few days &lt;a href="https://dev.to/cathylai/i-thought-my-one-sentence-is-enough-to-create-a-perfect-gpt-image-until-i-realized-it-had-been-3c20"&gt;learning about context engineering&lt;/a&gt;, I realised I could just send example images and describe what I like or dislike about them. That makes it a lot easier!! Both for me and for GPT!&lt;/p&gt;

&lt;h2&gt;
  
  
  "Before" Image
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqa4q2repo5zds5j4nmx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqa4q2repo5zds5j4nmx.jpeg" alt=" " width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Text prompt
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Could you give me an idea of how I could transform this area of the property to have a nicer garden?&lt;br&gt;
Please don't change any of the layout and proportions nor points of view. &lt;br&gt;
Please don't add extra buildings or structures either.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Result&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajaw9yknf87mf2ou30kk.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajaw9yknf87mf2ou30kk.jpeg" alt="Messy Garden" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conclusion: it looks very nice, but quite unrealistic... home owners probably won't be able to know where to start to achieve it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt with 2 Images as Positive and Negative Examples
&lt;/h2&gt;

&lt;p&gt;Positive example image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf6j84433p1xlvowci7i.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftf6j84433p1xlvowci7i.jpeg" alt="Positive training image" width="799" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use it as a style guide, eg the realism style, the lawns (restricted and tidy), the sunlight, the homeliness, and simple yet elegant furniture. Not expensive. And not gardening magazine fantasy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Negative example image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sf3xl1rkoweou64xbu3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sf3xl1rkoweou64xbu3.jpg" alt="Negative example image" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not match its unrealistic look, expensive furniture, perfect edges, storybook-like texture.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With 2 images as part of the parameters, I got a more achievable, yet still inspiring image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyacwqx6qi24oxpbdaryu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyacwqx6qi24oxpbdaryu.jpg" alt="More realistic garden after example images" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think it has picked up from the positive example: lawns, lighting, realism, and the simple furniture. From the negative, it got rid of the large qualities of flowers, and exclude expensive furniture. &lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;To easily test different variations, I have a test script&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;test-image-edit.mjs

// And run it like so:
npm run &lt;span class="nb"&gt;test&lt;/span&gt;:image-edit &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; public/images/messy_garden2.png &lt;span class="nt"&gt;--positive&lt;/span&gt; public/images/positive.jpeg &lt;span class="nt"&gt;--negative&lt;/span&gt; public/images/too_perfect.jpg &lt;span class="nt"&gt;-o&lt;/span&gt; scripts/output/test_output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This enables me to add images as parameters, so in the future I can play with different variations easily, without changing the source code constantly!&lt;/p&gt;

</description>
      <category>promptengineering</category>
      <category>ai</category>
      <category>devjournal</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
