<?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: ujja</title>
    <description>The latest articles on DEV Community by ujja (@ujja).</description>
    <link>https://dev.to/ujja</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%2F439943%2F774d42c6-75e1-4247-8688-30020ce8f40a.png</url>
      <title>DEV Community: ujja</title>
      <link>https://dev.to/ujja</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ujja"/>
    <language>en</language>
    <item>
      <title>🤖 What happens when your AI agent trusts outdated docs? I built Docgrity to find the gaps.</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Tue, 08 Sep 2026 11:07:13 +0000</pubDate>
      <link>https://dev.to/ujja/what-happens-when-your-ai-agent-trusts-outdated-docs-i-built-docgrity-to-find-the-gaps-485h</link>
      <guid>https://dev.to/ujja/what-happens-when-your-ai-agent-trusts-outdated-docs-i-built-docgrity-to-find-the-gaps-485h</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ujja/meet-docgrity-documentation-integrity-for-you-your-team-and-your-agent-friends-14gm" class="crayons-story__hidden-navigation-link"&gt;🚨📢Launching Docgrity 🚀: Documentation integrity for you, your team, and your agent friends. 🤖&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/cxu0m8UrYxo" title="🚨📢Launching Docgrity 🚀: Documentation integrity for you, your team, and your agent friends. 🤖"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ujja" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F439943%2F774d42c6-75e1-4247-8688-30020ce8f40a.png" alt="ujja profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ujja" class="crayons-story__secondary fw-medium m:hidden"&gt;
              ujja
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                ujja
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                
              
              &lt;div id="story-author-preview-content-4579531" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ujja" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F439943%2F774d42c6-75e1-4247-8688-30020ce8f40a.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;ujja&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ujja/meet-docgrity-documentation-integrity-for-you-your-team-and-your-agent-friends-14gm" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ujja/meet-docgrity-documentation-integrity-for-you-your-team-and-your-agent-friends-14gm" id="article-link-4579531"&gt;
          🚨📢Launching Docgrity 🚀: Documentation integrity for you, your team, and your agent friends. 🤖
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/documentation"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;documentation&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ujja/meet-docgrity-documentation-integrity-for-you-your-team-and-your-agent-friends-14gm" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;15&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ujja/meet-docgrity-documentation-integrity-for-you-your-team-and-your-agent-friends-14gm#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://ujjavala.github.io/docgrity-site/index.html" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;ujjavala.github.io&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/ujjavala/docgrity-vscode" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopengraph.githubassets.com%2Fa2f3a2d5b6e23e8e9ffd5ce3157625e793bcc0a63d7ea0e953c35658b2cf3a67%2Fujjavala%2Fdocgrity-vscode" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/ujjavala/docgrity-vscode" rel="noopener noreferrer" class="c-link"&gt;
            GitHub - ujjavala/docgrity-vscode: Find contradictions, duplicates and open questions in your repo's markdown docs — and raise GitHub issues to fix them. Powered by your own Copilot subscription. · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Find contradictions, duplicates and open questions in your repo's markdown docs — and raise GitHub issues to fix them. Powered by your own Copilot subscription. - ujjavala/docgrity-vscode
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://www.producthunt.com/products/docgrity" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;producthunt.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>ai</category>
      <category>tooling</category>
      <category>npm</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I built PadForward because sometimes you just need a pad</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Sat, 05 Sep 2026 13:39:59 +0000</pubDate>
      <link>https://dev.to/ujja/i-built-padforward-because-sometimes-you-just-need-a-pad-1ljl</link>
      <guid>https://dev.to/ujja/i-built-padforward-because-sometimes-you-just-need-a-pad-1ljl</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-09-03"&gt;Weekend Challenge: Generosity Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💗 Live demo:&lt;/strong&gt; &lt;a href="https://padforward-generosity.vercel.app/" rel="noopener noreferrer"&gt;padforward-generosity.vercel.app&lt;/a&gt; · &lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/ujjavala/padforward" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8t7bvqmenyltuwm844jg.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8t7bvqmenyltuwm844jg.png" alt="Landing page" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;A few days ago, I unexpectedly got my period while I was out.&lt;/p&gt;

&lt;p&gt;I hadn't planned for it, I didn't have a pad with me, and I happened to be near a train station, so I did what seemed like the obvious thing — I went looking for one there.&lt;/p&gt;

&lt;p&gt;I expected that a train station would have some way of helping with something as basic as a sanitary product.&lt;/p&gt;

&lt;p&gt;It didn't.&lt;/p&gt;

&lt;p&gt;I had money. I could have bought one if I could find a shop, and I had enough options to eventually figure it out. But in that moment, I still ended up using toilet paper.&lt;/p&gt;

&lt;p&gt;And that got me thinking.&lt;/p&gt;

&lt;p&gt;Not just about people who can't afford menstrual products, but about how &lt;strong&gt;needing a pad unexpectedly can happen to anyone&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You forget to put one in your bag. Your period comes earlier than expected. You thought you had one left but don't. You're travelling. You're somewhere unfamiliar. You simply didn't think you'd need one that day.&lt;/p&gt;

&lt;p&gt;It happens to the best of us.&lt;/p&gt;

&lt;p&gt;And then there is the other problem that nobody really talks about enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Does anyone have a pad?"
&lt;/h2&gt;

&lt;p&gt;Even if someone around me had a spare pad, would I actually walk up to them and ask?&lt;/p&gt;

&lt;p&gt;Maybe.&lt;/p&gt;

&lt;p&gt;But probably not.&lt;/p&gt;

&lt;p&gt;We say periods are normal, and they absolutely are. But that doesn't mean asking a random person for a pad in a crowded train station suddenly feels completely normal.&lt;/p&gt;

&lt;p&gt;There is that little moment of hesitation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do I really want to ask someone?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Who do I ask?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What if they don't have one?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What if this becomes awkward?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And if you're already stressed because you've unexpectedly got your period, the last thing you want is another problem to solve.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;PadForward&lt;/strong&gt; came from.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, what is PadForward?
&lt;/h3&gt;

&lt;p&gt;PadForward is a community-powered network for finding and donating menstrual products in public places. There are already organisations doing important work like distributing free period products, installing vending machines and helping people find access points. PadForward isn't trying to replace those efforts. I'm interested in what happens when you add a community-powered, supply-aware layer on top of them.&lt;/p&gt;

&lt;p&gt;The basic idea is pretty simple: &lt;strong&gt;if you need a pad, find one nearby without having to ask someone, and if you have spare pads, help put them somewhere they're likely to be needed.&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzz7jzeyiyyeytf0u192z.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzz7jzeyiyyeytf0u192z.png" alt="Findign a pad" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Think of it as a community layer sitting on top of places people already use — train stations, bus stations, universities, workplaces, community centres and eventually other public spaces.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa6ibqf0t5mh7bbz0jhes.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa6ibqf0t5mh7bbz0jhes.png" alt="Station view" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Someone might donate 20 pads today.&lt;/p&gt;

&lt;p&gt;Someone else might need one tomorrow.&lt;/p&gt;

&lt;p&gt;Those two people never have to meet.&lt;/p&gt;

&lt;p&gt;They don't need to know each other's names.&lt;/p&gt;

&lt;p&gt;They don't even need to know who helped whom.&lt;/p&gt;

&lt;p&gt;PadForward simply connects the two.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you need one, you shouldn't have to ask
&lt;/h3&gt;

&lt;p&gt;The first thing I wanted to get right was the emergency experience.&lt;/p&gt;

&lt;p&gt;Imagine you're at Central Station and suddenly realise you need a pad.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgif4wq71sn9087m5m4x.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgif4wq71sn9087m5m4x.png" alt="Central Bus Interchange" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You open PadForward and tap &lt;strong&gt;"I Need a Pad."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You don't need an account.&lt;/p&gt;

&lt;p&gt;You don't need to explain what happened.&lt;/p&gt;

&lt;p&gt;You don't need to post a request.&lt;/p&gt;

&lt;p&gt;You just see what's nearby.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Central Station&lt;/strong&gt;&lt;br&gt;
🟢 Available&lt;br&gt;
Verified 12 minutes ago&lt;br&gt;
4 min walk&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Town Hall&lt;/strong&gt;&lt;br&gt;
🟡 A few reported&lt;br&gt;
Verified 28 minutes ago&lt;br&gt;
6 min walk&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Museum&lt;/strong&gt;&lt;br&gt;
🔴 None reported&lt;br&gt;
Verified 18 minutes ago&lt;br&gt;
3 min walk&lt;/p&gt;

&lt;p&gt;You pick the closest available location and get directions.&lt;/p&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;No awkward conversation required.&lt;/p&gt;

&lt;p&gt;And afterwards, instead of asking for anything back, PadForward can simply say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Someone helped you today. When you're able, help someone else.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  But where do the pads come from?
&lt;/h3&gt;

&lt;p&gt;This is where the other half of PadForward comes in.&lt;/p&gt;

&lt;p&gt;Maybe I have a packet of pads at home and want to donate some.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fod5zg913k767zkjkv13o.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fod5zg913k767zkjkv13o.png" alt="Donate" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't necessarily know which station needs them.&lt;/p&gt;

&lt;p&gt;I don't want to spend an hour researching where to take them.&lt;/p&gt;

&lt;p&gt;So I can tell PadForward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I have 20 pads. Where should I donate them?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;PadForward looks at the current community supply information and the need at different locations and recommends somewhere that needs them.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdav5kmey4h5lzdf8a8yb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdav5kmey4h5lzdf8a8yb.png" alt="Donate in station" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Museum Station&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🔴 Critical&lt;br&gt;
Need Score: 94/100&lt;br&gt;
No recent community supply reported&lt;br&gt;
High estimated demand&lt;/p&gt;

&lt;p&gt;So instead of simply donating somewhere, I'm donating &lt;strong&gt;where the network needs them most&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the part I really like about this idea.&lt;/p&gt;

&lt;p&gt;It turns a small thing sitting in my cupboard into something that might be exactly where another person needs it.&lt;/p&gt;
&lt;h3&gt;
  
  
  And yes, I'm using Solana for the donation
&lt;/h3&gt;

&lt;p&gt;I also wanted the Solana integration to actually mean something in the product, rather than adding it just so I could say I used it :)&lt;/p&gt;

&lt;p&gt;So PadForward has a &lt;strong&gt;"Donate a Pad"&lt;/strong&gt; flow using Solana.&lt;/p&gt;

&lt;p&gt;The idea is that you can sponsor a pad — or several pads — through a Solana transaction, with the donation recorded transparently on-chain.&lt;/p&gt;

&lt;p&gt;The physical fulfilment happens through the PadForward community and partner network, so the blockchain isn't pretending to magically move a physical pad from one place to another.&lt;/p&gt;

&lt;p&gt;Instead, it gives us a transparent digital record of the generosity behind it.&lt;/p&gt;

&lt;p&gt;So the flow becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Donate a pad → Solana transaction → PadForward donation → community supply point → someone can access a pad when they need one.&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgy87xd72hc7vhmkoj8et.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgy87xd72hc7vhmkoj8et.png" alt="Solana donation" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I like this because the unit of generosity is really easy to understand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One donation. One pad. One small act that could make someone's day a little easier.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And you don't need to be a crypto person to use it.&lt;/p&gt;

&lt;p&gt;The blockchain is underneath the experience rather than becoming the experience.&lt;/p&gt;
&lt;h3&gt;
  
  
  I didn't want another chatbot
&lt;/h3&gt;

&lt;p&gt;The other technology I really wanted to use properly was Google AI.&lt;/p&gt;

&lt;p&gt;I could have added a chatbot that answered questions about periods, but that didn't feel particularly useful.&lt;/p&gt;

&lt;p&gt;Instead, PadForward has an AI agent that can actually interact with the application.&lt;/p&gt;

&lt;p&gt;You can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I suddenly got my period and I'm at Central."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent can understand that you need a nearby pad, find relevant locations, check their current community supply and help you choose one.&lt;/p&gt;

&lt;p&gt;Or you can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I have 20 pads to donate."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent can look at the current need across nearby stations and recommend where they would be most useful.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyv6etv3r7evitqreh6if.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyv6etv3r7evitqreh6if.png" alt="Where to donate" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can even report something naturally:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The donation box at Town Hall is empty."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI can understand that this is a supply report and pass it through the application's backend rather than just replying with a paragraph of text.&lt;/p&gt;

&lt;p&gt;So Gemini isn't there just to have a conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's there to help the network make decisions and take actions.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  And what if the AI isn't available at all?
&lt;/h2&gt;

&lt;p&gt;This mattered to me, because the moment you most need PadForward is exactly the moment you might be underground at a train station with one bar of signal.&lt;/p&gt;

&lt;p&gt;So the assistant degrades gracefully instead of just failing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gemini first.&lt;/strong&gt; When the server and the Gemini API are reachable, the full agent runs — real tool calls, live network data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your browser's built-in AI second.&lt;/strong&gt; If the network is unreachable, PadForward tries the browser's built-in on-device AI (Chrome's Prompt API, powered by Gemini Nano). It runs entirely offline, grounded in the last station data the app cached — and it's told explicitly to never invent availability and to remind you the data may be stale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain heuristics last.&lt;/strong&gt; If there's no built-in AI either, a small deterministic engine answers from the cached map data: closest place that last reported supply if you need a pad, highest need score if you want to donate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The answer might get simpler at each step down, but you never get a spinner and a shrug.&lt;/p&gt;
&lt;h3&gt;
  
  
  Snap the box — AI that counts pads from a photo
&lt;/h3&gt;

&lt;p&gt;Supply reports are the lifeblood of the network, so I wanted reporting to take &lt;em&gt;zero&lt;/em&gt; effort.&lt;/p&gt;

&lt;p&gt;On any station page — and right inside the Find flow when you pick up a pad — instead of counting pads and tapping buttons, you can just photograph the donation box. Gemini vision looks at the photo and returns a strict JSON estimate — count, confidence, and a one-line description — which prefills a real supply report you confirm with one tap.&lt;/p&gt;

&lt;p&gt;And it follows the same philosophy as the assistant: if you're offline, the exact same flow runs on your &lt;strong&gt;browser's built-in multimodal AI&lt;/strong&gt; (Chrome's Prompt API), entirely on-device. The photo is analyzed and discarded either way — never uploaded, never stored.&lt;/p&gt;
&lt;h3&gt;
  
  
  What if the map says there are pads, but there aren't?
&lt;/h3&gt;

&lt;p&gt;This was another thing I didn't want to fake.&lt;/p&gt;

&lt;p&gt;Real-world inventory is messy.&lt;/p&gt;

&lt;p&gt;I can't claim that PadForward always knows exactly how many pads are sitting inside a station.&lt;/p&gt;

&lt;p&gt;Instead, the community helps keep the information fresh.&lt;/p&gt;

&lt;p&gt;Anyone can report:&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;Plenty&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🟡 &lt;strong&gt;A few&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🔴 &lt;strong&gt;None&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚪ &lt;strong&gt;Not sure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The report also has a timestamp, so you might see:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🟢 Available — verified 14 minutes ago&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;rather than pretending the information is permanently accurate.&lt;/p&gt;

&lt;p&gt;Reports from community champions and multiple recent reports can give us more confidence, while old or conflicting reports can lower it.&lt;/p&gt;

&lt;p&gt;Over time, this could become a really interesting data problem too — figuring out where demand is happening and when stations tend to run low.&lt;/p&gt;
&lt;h3&gt;
  
  
  The people who keep it going
&lt;/h3&gt;

&lt;p&gt;Some people might want to do more than make a one-off donation.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Pad Champions&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;You could adopt a local station and help keep an eye on its community supply.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F05348suzhdi3jzlebbfs.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F05348suzhdi3jzlebbfs.png" alt="Pad Champion" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A champion could verify that a donation point still has supplies, report when it is empty, confirm a restock or help coordinate donations.&lt;/p&gt;

&lt;p&gt;The idea is that PadForward shouldn't just be a map that people open when they have a problem.&lt;/p&gt;

&lt;p&gt;It should become a little community network that people help maintain.&lt;/p&gt;
&lt;h3&gt;
  
  
  The bigger idea is the network
&lt;/h3&gt;

&lt;p&gt;This is the part that excites me most.&lt;/p&gt;

&lt;p&gt;The actual product isn't really the map.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F25dwlfrl8pdvt54q29vv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F25dwlfrl8pdvt54q29vv.png" alt="Impact" width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's the network behind it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Someone has spare pads
          ↓
PadForward finds where they're needed
          ↓
Community supply point
          ↓
Someone unexpectedly needs one
          ↓
They find it without asking
          ↓
When they're able, they pay it forward
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The donor may never know who received the pad.&lt;/p&gt;

&lt;p&gt;The person who received it may never know who donated it.&lt;/p&gt;

&lt;p&gt;And that's completely fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generosity doesn't always need a face attached to it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes it can just be:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Someone left one for you."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  There's a lot more I want to do with it
&lt;/h3&gt;

&lt;p&gt;For the hackathon, I'm starting with public transport locations because they make the problem very tangible.&lt;/p&gt;

&lt;p&gt;But the same idea could work in universities, workplaces, libraries, community centres, airports, shelters and other places where people spend time.&lt;/p&gt;

&lt;p&gt;I'd also like to explore route-based discovery, so someone travelling from Parramatta to Central could ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is there somewhere I can get a pad without making a big detour?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is also a lot of interesting work to do around demand prediction, restock alerts, voice accessibility, multilingual support and using analytics to understand where the network needs help.&lt;/p&gt;

&lt;p&gt;Snowflake could help with the longer-term demand and supply analytics, while ElevenLabs could make the experience more accessible through voice.&lt;/p&gt;

&lt;p&gt;But those are extensions.&lt;/p&gt;

&lt;p&gt;The core idea stays the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why PadForward?
&lt;/h3&gt;

&lt;p&gt;I started with a very ordinary, slightly inconvenient experience.&lt;/p&gt;

&lt;p&gt;I unexpectedly needed a pad.&lt;/p&gt;

&lt;p&gt;I couldn't find one.&lt;/p&gt;

&lt;p&gt;And I realised that even though I had the means to solve the problem eventually, &lt;strong&gt;getting something so basic wasn't as easy as it should have been.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That made me think about everyone else who might find themselves in the same situation.&lt;/p&gt;

&lt;p&gt;Not just someone who can't afford a pad.&lt;/p&gt;

&lt;p&gt;Anyone.&lt;/p&gt;

&lt;p&gt;Because sometimes you forget.&lt;/p&gt;

&lt;p&gt;Sometimes you don't expect it.&lt;/p&gt;

&lt;p&gt;Sometimes you're just in the wrong place at the wrong time.&lt;/p&gt;

&lt;p&gt;And sometimes there might actually be someone nearby who has exactly what you need — but asking them feels like a bigger deal than it should.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PadForward is my attempt to remove that gap.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No asking.&lt;/p&gt;

&lt;p&gt;No explaining.&lt;/p&gt;

&lt;p&gt;Just access.&lt;/p&gt;

&lt;p&gt;And if you have one to spare?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pass it forward. 💗&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The demo is live at &lt;strong&gt;&lt;a href="https://padforward-generosity.vercel.app/" rel="noopener noreferrer"&gt;padforward-generosity.vercel.app&lt;/a&gt;&lt;/strong&gt; — no account, no sign-up.&lt;/p&gt;

&lt;p&gt;A few things to try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://padforward-generosity.vercel.app/find" rel="noopener noreferrer"&gt;Find a pad&lt;/a&gt;&lt;/strong&gt; — share your location (or browse the demo stations) and see nearby community supply with walking times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://padforward-generosity.vercel.app/donate" rel="noopener noreferrer"&gt;Donate&lt;/a&gt;&lt;/strong&gt; — ask &lt;em&gt;"Where should my donation go?"&lt;/em&gt;, pick the critical station, and watch it flip from 🔴 to 🟢 — optionally paying with SOL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://padforward-generosity.vercel.app/assistant" rel="noopener noreferrer"&gt;Ask PadForward&lt;/a&gt;&lt;/strong&gt; — try &lt;em&gt;"I need a pad nearby"&lt;/em&gt;, &lt;em&gt;"I have 20 pads to donate"&lt;/em&gt;, or &lt;em&gt;"The donation box at Town Hall is empty"&lt;/em&gt; and watch the agent call real application tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://padforward-generosity.vercel.app/impact" rel="noopener noreferrer"&gt;Impact&lt;/a&gt;&lt;/strong&gt; — see the network's live numbers update as you interact.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;All stations are clearly-labelled &lt;strong&gt;demo community points&lt;/strong&gt; at real Sydney station coordinates — supply figures are demo data, not real inventory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ujjavala/padforward" rel="noopener noreferrer"&gt;https://github.com/ujjavala/padforward&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;For the hackathon the whole product ships as a &lt;strong&gt;single Next.js 14 app on Vercel&lt;/strong&gt; — the UI plus API route handlers with an in-memory demo store. The &lt;strong&gt;designed architecture&lt;/strong&gt; (FastAPI + PostgreSQL/PostGIS behind the exact same API contract) lives in &lt;code&gt;services/api&lt;/code&gt; with docker-compose and a full pytest suite; the frontend switches to it with one env var.&lt;/p&gt;

&lt;p&gt;The interesting pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An agent, not a chatbot (Google AI).&lt;/strong&gt; Gemini 2.0 Flash gets a registry of 11 validated application tools (&lt;code&gt;find_nearby_stations&lt;/code&gt;, &lt;code&gt;get_highest_need_locations&lt;/code&gt;, &lt;code&gt;report_supply_status&lt;/code&gt;, &lt;code&gt;create_donation&lt;/code&gt;, …) and decides which to call — multi-round function calling, and it may only state availability that comes from tool results. It can never touch the data store directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A four-tier AI fallback chain.&lt;/strong&gt; Gemini → a deterministic intent engine using the &lt;em&gt;same tools&lt;/em&gt; → the browser's built-in on-device AI (Chrome Prompt API / Gemini Nano) grounded in cached station data when you're fully offline → plain heuristics over the cached map. The answer gets simpler at each step, but you never get a spinner and a shrug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vision supply reports.&lt;/strong&gt; Photograph a donation box and Gemini vision (or the browser's built-in multimodal AI when offline) counts the pads and prefills a supply report — photo analyzed and discarded, never stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana donations.&lt;/strong&gt; Sponsoring pads produces a quote (pads → SOL) and records the donation with a transaction signature. With devnet enabled, it submits a &lt;strong&gt;real Memo transaction to Solana devnet&lt;/strong&gt; (auto-airdrop funded) and links to Solana Explorer; otherwise it's transparently labelled &lt;em&gt;devnet (simulated)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A deterministic, ML-ready need score.&lt;/strong&gt; Every station gets a 0–100 score (40% supply shortage, 20% estimated demand, 15% staleness, 15% recent requests, 10% historical demand) in an isolated module — that's what routes donations to where they help most.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust over false precision.&lt;/strong&gt; Supply is community-reported (🟢/🟡/🔴/⚪) with timestamps and a confidence model — champion reports and agreeing recent reports raise confidence, conflicts lower it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Google AI&lt;/strong&gt; — Gemini function-calling agent over real application tools, vision-based photo supply reports, plus the offline Chrome built-in AI (Gemini Nano) fallback for both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Solana&lt;/strong&gt; — donation flow with SOL quotes and on-chain (devnet) Memo transaction records — the blockchain underneath the experience, not the experience.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>gemini</category>
      <category>solana</category>
    </item>
    <item>
      <title>🚨📢Launching Docgrity 🚀: Documentation integrity for you, your team, and your agent friends. 🤖</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Sat, 05 Sep 2026 04:55:38 +0000</pubDate>
      <link>https://dev.to/ujja/meet-docgrity-documentation-integrity-for-you-your-team-and-your-agent-friends-14gm</link>
      <guid>https://dev.to/ujja/meet-docgrity-documentation-integrity-for-you-your-team-and-your-agent-friends-14gm</guid>
      <description>&lt;p&gt;Today I'm launching Docgrity — a tool for finding where your documentation disagrees with itself, before it confuses you or the next AI agent working on your codebase.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;br&gt;
Docgrity is a documentation integrity tool for repos and Confluence. It finds duplicate or contradictory documentation, surfaces unanswered questions, and gives you the evidence behind each finding so you can decide what needs fixing.&lt;br&gt;
Try it here:&lt;br&gt;
VS Code / CLI / GitHub Actions: &lt;a href="https://github.com/ujjavala/docgrity-vscode" rel="noopener noreferrer"&gt;https://github.com/ujjavala/docgrity-vscode&lt;/a&gt;&lt;br&gt;
VS Code site: &lt;a href="https://ujjavala.github.io/docgrity-vscode-site/index.html" rel="noopener noreferrer"&gt;https://ujjavala.github.io/docgrity-vscode-site/index.html&lt;/a&gt;&lt;br&gt;
CLI: &lt;a href="https://www.npmjs.com/package/docgrity" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/docgrity&lt;/a&gt;&lt;br&gt;
Confluence: &lt;a href="https://ujjavala.github.io/docgrity-site/index.html" rel="noopener noreferrer"&gt;https://ujjavala.github.io/docgrity-site/index.html&lt;/a&gt;&lt;br&gt;
And if you’re feeling extra supportive, feel free to show the launch some love on Product Hunt or Crowdstax. ❤️&lt;br&gt;
Product Hunt: &lt;a href="https://www.producthunt.com/products/docgrity" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/docgrity&lt;/a&gt;&lt;br&gt;
Crowdstax: &lt;a href="https://www.crowdstax.com/products/docgrity" rel="noopener noreferrer"&gt;https://www.crowdstax.com/products/docgrity&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The idea came from a pretty simple problem. I opened one of my own docs recently and realised I didn't believe a word of it.&lt;/p&gt;

&lt;p&gt;AI-assisted coding has a side effect nobody really talks about: &lt;strong&gt;every agent session leaves documentation behind.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PLAN.md&lt;/code&gt;. &lt;code&gt;TODO.md&lt;/code&gt;. Implementation notes. Architecture docs. Then another architecture doc because the agent didn't notice the first one.&lt;/p&gt;

&lt;p&gt;Eventually, you have a repo full of markdown and no idea what's still true.&lt;/p&gt;

&lt;p&gt;And now there's a bigger problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agents read those docs too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stale or contradictory documentation isn't just confusing for developers anymore. It's context for the next agent.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Docgrity&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with your repo
&lt;/h2&gt;

&lt;p&gt;Docgrity scans your repository's Markdown and finds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Duplicates&lt;/strong&gt; — docs saying essentially the same thing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contradictions&lt;/strong&gt; — conflicting claims, shown side by side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open questions&lt;/strong&gt; — unanswered questions and stale TODOs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And every finding comes with evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No finding without a receipt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The source text is quoted verbatim and verified against the actual file before a finding is reported. If the model can't prove it, the finding gets dropped.&lt;/p&gt;

&lt;h3&gt;
  
  
  VS Code
&lt;/h3&gt;

&lt;p&gt;An interactive dashboard inside VS Code, with clickable evidence and draft GitHub issues you approve before posting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code &lt;span class="nt"&gt;--install-extension&lt;/span&gt; ujjavala.docgrity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://ujjavala.github.io/docgrity-vscode-site/index.html" rel="noopener noreferrer"&gt;Docgrity for VS Code&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  CLI
&lt;/h3&gt;

&lt;p&gt;Prefer the terminal? The CLI is read-only and local-first, with Ollama by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; docgrity
docgrity scan &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.npmjs.com/package/docgrity" rel="noopener noreferrer"&gt;Docgrity on npm&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Action
&lt;/h3&gt;

&lt;p&gt;You can also run documentation checks in CI, with deduplicated issues that automatically close when findings are resolved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ujjavala/docgrity-vscode/action@main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ujjavala/docgrity-vscode" rel="noopener noreferrer"&gt;Docgrity on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  And now, Confluence
&lt;/h2&gt;

&lt;p&gt;Docs don't only live in repositories.&lt;/p&gt;

&lt;p&gt;I'm also building &lt;strong&gt;Docgrity for Confluence&lt;/strong&gt;, an early-stage Forge app that scans your Confluence spaces for the same problems: duplicates, contradictions and unanswered questions.&lt;/p&gt;

&lt;p&gt;It's currently available through a direct install rather than the Atlassian Marketplace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://ujjavala.github.io/docgrity-site/index.html" rel="noopener noreferrer"&gt;Install / try Docgrity for Confluence&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's early, but the idea is the same: make the knowledge your team — and your AI agents — rely on a little more trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero infrastructure
&lt;/h2&gt;

&lt;p&gt;I wanted Docgrity to be useful without adding another service that needs to own your documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero infrastructure. No servers. No telemetry. MIT licensed.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Documentation integrity for you, your team, and your agent friends. 🤖&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run a scan on a repo or Confluence space you think you know.&lt;/p&gt;

&lt;p&gt;If it finds something that makes you go &lt;em&gt;"wait… that's still in there?"&lt;/em&gt; — I'd love to hear about it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>showdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>AI Engineering Is Easy. Changing How We Work Is Hard</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Fri, 04 Sep 2026 09:24:10 +0000</pubDate>
      <link>https://dev.to/ujja/ai-engineering-is-easy-changing-how-we-work-is-hard-39j4</link>
      <guid>https://dev.to/ujja/ai-engineering-is-easy-changing-how-we-work-is-hard-39j4</guid>
      <description>&lt;p&gt;AI engineering sounds fancy. New terms are everywhere: agentic development, AI-native engineering, spec-driven development, and now AI harness engineering. Underneath all the terminology, though, something genuinely useful is happening. AI can now help with requirements, challenge a &lt;a href="https://en.wikipedia.org/wiki/Product_requirements_document" rel="noopener noreferrer"&gt;PRD&lt;/a&gt;, explore UX ideas, reason about architecture, create implementation plans, write code and validate the result.&lt;/p&gt;

&lt;p&gt;The obvious question is what AI can do. The more interesting question is whether the way we build software is ready for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The workflow is changing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A workflow we've been exploring breaks development into five stages: &lt;strong&gt;requirements, refinement, planning, build and validation&lt;/strong&gt;. The stages themselves aren't new, but AI can now participate in each one. It can take existing product inputs, help clarify the problem, question assumptions, identify gaps in a PRD and then turn a well-defined requirement into a plan and eventually implementation tasks.&lt;/p&gt;

&lt;p&gt;This puts more emphasis on the quality of the requirements. A human involved in a project might understand what “improve the experience” means because they've had several conversations about it. An agent doesn't have that shared history. It needs the problem, scope, constraints, edge cases and expected outcome to be explicit. That doesn't mean writing enormous specifications; it means using AI to help make the requirements precise before we start building.&lt;/p&gt;

&lt;p&gt;AI can actually be a useful, slightly annoying reviewer here, asking what happens when something fails, whether a requirement is testable, whether two parts of the document contradict each other and what we haven't considered yet. It can also help compare different versions of a PRD or have one model review another's output, making gaps easier to spot. The important part is that AI is helping us uncover ambiguity, not making the decisions for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Maybe coding isn't the bottleneck&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This becomes more interesting when we look at where teams actually spend their time. Complex work can involve several rounds between product, UX, requirements and engineering before development can properly begin. That's often necessary, but it can also create significant bottlenecks. If an AI agent can produce a working implementation quickly, waiting two weeks for a requirement to be clarified becomes a much bigger problem than it used to be.&lt;/p&gt;

&lt;p&gt;This suggests that engineers need to be involved earlier rather than receiving requirements only once they're considered finished. UX needs to be part of the conversation early too. A rough prototype or wireframe can expose gaps in a requirement much faster than another round of discussion, and AI makes creating those lightweight prototypes much cheaper. Requirements, UX and technical design can become an iterative loop rather than a series of handoffs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;More documentation isn't always better&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The instinct with AI is often to give it more documentation: more PRDs, more architecture diagrams, more wiki pages and more context. But if several documents describe the same capability differently, we're not really giving the agent better context; we're giving it more ways to get confused.&lt;/p&gt;

&lt;p&gt;What the agent needs is a way to navigate the system. A clear project structure, focused documentation, useful agent instructions, architecture decisions that explain why something exists and a well-organised codebase can be far more useful than one giant specification. The goal isn't to give AI everything we know. It is to make it easy for AI to find what it needs when it needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The ticket might be the problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AI also makes me question how we slice work. A ticket that takes weeks or months is difficult for an agent to reason about, and arguably isn't a particularly useful unit of work for humans either. “Build authentication” is very different from breaking the problem into password reset, token validation, password updates and the associated tests.&lt;/p&gt;

&lt;p&gt;That doesn't mean turning every feature into dozens of tiny tickets. We just need work that has a clear purpose, manageable scope and a definition of done. If something takes months, it might be a project hiding in a Jira costume.&lt;/p&gt;

&lt;p&gt;At the same time, not everything needs the full AI lifecycle. A large feature may benefit from structured requirements, refinement, planning and validation, while a small BAU change might only need a prompt and a developer. If we apply the same process to everything, we risk replacing one form of bureaucracy with another. The workflow should match the complexity of the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The agent needs to see the real system&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There is also a fairly fundamental requirement: &lt;strong&gt;the agent needs to understand the real system&lt;/strong&gt;. Giving it a PRD without access to the relevant codebase means it is still making assumptions. With the code, it can find existing patterns, understand constraints, reuse functionality and spot when a proposed solution doesn't fit.&lt;/p&gt;

&lt;p&gt;Of course, giving AI access to source code introduces security, licensing, privacy and organisational considerations, so AI adoption isn't simply a matter of choosing the right model. Some of the biggest barriers have nothing to do with the model at all.&lt;/p&gt;

&lt;p&gt;And once multiple developers and agents are working in parallel, things get even more interesting. Each agent has its own context and can make decisions based on what it currently sees. One agent can change something another doesn't know about, or two agents can make perfectly reasonable decisions that don't work well together. This makes good engineering practices even more important: small changes, clear boundaries, good tests, frequent reviews and consistent project rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;So, are we ready?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Probably not completely, and that's fine. We don't need to jump straight into autonomous software development. We can take real pieces of work, try these workflows, see where they break and improve the process as we go.&lt;/p&gt;

&lt;p&gt;Because AI isn't just changing how quickly we write code. It is exposing everything around the code that slows us down: unclear requirements, oversized tickets, late UX involvement, documentation drift, handoffs, access restrictions and decisions that take days to make.&lt;/p&gt;

&lt;p&gt;That's why I think the idea of an &lt;strong&gt;AI harness&lt;/strong&gt; is bigger than prompts and agent configuration. The harness is the environment we create around the AI: how we define work, how product, UX and engineering collaborate, how knowledge is structured, how repositories are organised, how work is validated and what access the agent has.&lt;/p&gt;

&lt;p&gt;The AI might be the new part, but the way we work around it is what will determine whether it actually makes us faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI engineering is easy. Changing how we work is hard.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>software</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Love Dogs, But Dogs Scare Me — So I Built Pawsitive with Gemini 🐾.</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Sat, 15 Aug 2026 09:28:28 +0000</pubDate>
      <link>https://dev.to/ujja/i-love-dogs-but-dogs-scare-me-so-i-built-pawsitive-with-gemini--1536</link>
      <guid>https://dev.to/ujja/i-love-dogs-but-dogs-scare-me-so-i-built-pawsitive-with-gemini--1536</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-08-13"&gt;Weekend Challenge: Dog Days Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are only two types of people in the world: those who love dogs, and those who love them from a distance. 😂🐶&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;I love dogs.&lt;/p&gt;

&lt;p&gt;There, I said it.&lt;/p&gt;

&lt;p&gt;And yet, I am also scared of them.&lt;/p&gt;

&lt;p&gt;It is a strange combination. I can happily watch dog videos for hours, admire every dog I see on the street, and still instinctively tense up when one suddenly runs towards me.&lt;/p&gt;

&lt;p&gt;A lot of that comes from a traumatic experience I had with a dog as a child. You can grow up knowing that one experience doesn't define every dog you will ever meet, but sometimes your instincts don't get the memo.&lt;/p&gt;

&lt;p&gt;While thinking about this challenge, I started wondering if the problem was partly &lt;strong&gt;not understanding what I was seeing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If a dog is wagging its tail, what does that actually mean? If it is staring at me, should I move away? If it is barking, is it excited, nervous, protective, or something else? And if a dog is approaching me on a footpath, what should I actually do?&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu70a240to1r75z0exvxg.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu70a240to1r75z0exvxg.png" alt=" " width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That question became &lt;strong&gt;Pawsitive&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pawsitive is an interactive learning app for people who feel nervous around dogs. Instead of telling people not to be afraid, it tries to make encounters feel less unpredictable by teaching them how to recognise common body-language signals, understand situations, and make calmer decisions.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1620gicx87zoj32d5pue.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1620gicx87zoj32d5pue.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But then I realised there was another side to the interaction.&lt;/p&gt;

&lt;p&gt;A dog owner might see their dog happily walking towards someone and think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Don't worry, he's friendly!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The person approaching might be thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Please don't let that dog come any closer."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both people can be looking at the same dog while experiencing completely different situations.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2jne5ulf7zvh9i71zbeq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2jne5ulf7zvh9i71zbeq.png" alt=" " width="799" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So Pawsitive has two learning paths: &lt;strong&gt;people who are nervous around dogs and dog owners&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The first helps people understand dogs and build confidence. The second helps owners recognise when someone might be uncomfortable, why giving people space matters, and why "my dog is friendly" doesn't necessarily make an approaching dog less intimidating.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpn8tbzsefilxqceryl1r.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpn8tbzsefilxqceryl1r.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That became the idea behind the whole app:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Two perspectives. One better interaction.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Github link: &lt;a href="https://github.com/ujjavala/pawsitive" rel="noopener noreferrer"&gt;https://github.com/ujjavala/pawsitive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vercel deployment: &lt;a href="https://pawsitive-nine.vercel.app/" rel="noopener noreferrer"&gt;https://pawsitive-nine.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The Vercel deployment does not include a Gemini API key. On supported desktop Chrome devices, &lt;strong&gt;Understand This Dog&lt;/strong&gt; falls back to Chrome's built-in Gemini Nano model and analyses the photo privately on-device. You can also run the app locally and add &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; to &lt;code&gt;.env.local&lt;/code&gt; for server-side Gemini analysis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The experience begins with three gentle onboarding questions: how comfortable you are around unfamiliar dogs, which situations feel most uncomfortable, and what you want to achieve. Someone who selects "Very nervous" isn't immediately thrown into a stressful scenario. They start by learning the basics, exploring relaxed and tense body language before gradually moving into realistic situations. Their answer also sets an initial confidence score, but it never locks content or forces them into an interaction.&lt;/p&gt;

&lt;p&gt;Then comes &lt;strong&gt;What Would You Do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You might be walking down a footpath when a dog approaches with its owner. Instead of reading instructions, you decide how you would respond and then get an explanation of why one choice may lead to a calmer interaction.&lt;/p&gt;

&lt;p&gt;The perspective can then switch to the owner's side. The same encounter looks very different when you realise that giving someone space may be more helpful than reassuring them that your dog is friendly.&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;Understand This Dog&lt;/strong&gt; lets users upload a dog photo and use Gemini to explore the visible body-language cues. The result also suggests a possible breed or breed mix, explains which visible features informed the estimate, and shares typical physical characteristics and cautiously worded breed-level behavioural tendencies. It uses the configured server model when a Gemini key is available and falls back to Chrome's built-in Gemini Nano model when the server has no key and the browser supports multimodal on-device AI.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fux70ta2yxdl1p9q0bn6w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fux70ta2yxdl1p9q0bn6w.png" alt=" " width="799" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The entire experience is designed to be short enough to explore in a few minutes while still telling the complete story.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I wanted Pawsitive to feel like something you would actually want to use, rather than a clinical safety course.&lt;/p&gt;

&lt;p&gt;That led to a bright, playful interface, an animated SVG dog, playful scenario scenes, gentle sounds, small celebrations, and a mascot named &lt;strong&gt;Pip&lt;/strong&gt; who follows the user through the experience.&lt;/p&gt;

&lt;p&gt;The app is built with &lt;strong&gt;React, TypeScript and Vite&lt;/strong&gt;, with &lt;strong&gt;Tailwind CSS and Radix UI primitives&lt;/strong&gt; for the interface, &lt;strong&gt;Motion for React&lt;/strong&gt; (the current Framer Motion package) for animation, SVG illustrations for Pip, and &lt;strong&gt;Zustand with localStorage&lt;/strong&gt; for progress tracking. &lt;strong&gt;Zod&lt;/strong&gt; validates structured AI responses at runtime, while &lt;strong&gt;Lucide&lt;/strong&gt; provides the icon system. Server inference uses Google's &lt;code&gt;@google/genai&lt;/code&gt; SDK, while private inference uses Chrome's built-in &lt;code&gt;LanguageModel&lt;/code&gt; Prompt API without adding another model runtime to the application bundle.&lt;/p&gt;

&lt;p&gt;There is deliberately no authentication or database in the MVP. You can open the app and start learning immediately.&lt;/p&gt;

&lt;p&gt;The weekend build includes &lt;strong&gt;10 lessons for nervous users, six owner lessons, eight scenarios, eight body-language signals and five gentle achievements&lt;/strong&gt;. Routes are lazy-loaded so the non-critical learning, scenario and AI screens do not all have to load up front.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making Pip feel alive
&lt;/h3&gt;

&lt;p&gt;Pip changes depending on what is happening. They can tilt their head during a question, wag their tail after a correct answer, bounce when a lesson is completed, or look confused when something goes wrong.&lt;/p&gt;

&lt;p&gt;The animations are deliberately gentle. There are no sudden dogs jumping towards the screen or unexpected barking, because an app designed for nervous dog lovers shouldn't accidentally make them nervous.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz84xo0804h9gp6j9dkpu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz84xo0804h9gp6j9dkpu.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same thinking shaped the sound design. The current build uses short, synthesised feedback tones for correct answers, lesson completion and soft errors. I deliberately left barking and other dog sounds out of the learning flow so the app cannot unexpectedly startle someone. Sound can be disabled, and motion can be reduced from inside the app. Pip also respects the device's own reduced-motion preference.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxho88m7itul4q5n8xz2w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxho88m7itul4q5n8xz2w.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal is simple: &lt;strong&gt;make the app feel alive without making it overwhelming.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 The Gemini Experiment
&lt;/h2&gt;

&lt;p&gt;The most interesting part of Pawsitive is &lt;strong&gt;Understand This Dog&lt;/strong&gt;, where I wanted AI to do something more meaningful than power another generic chatbot.&lt;/p&gt;

&lt;p&gt;A user can upload a JPG, PNG or WebP photo of a dog, and Gemini analyses the visible signals in the image. The response is structured into six parts:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhf7mz71mbqg8e5qfklln.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhf7mz71mbqg8e5qfklln.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possible breed&lt;/strong&gt; — an appearance-based breed or breed-mix estimate, the visible traits behind it, and an honest visual-confidence level. If the image is not clear enough, the model can simply return &lt;strong&gt;Breed unclear&lt;/strong&gt; instead of forcing a guess.&lt;/p&gt;

&lt;p&gt;The breed section also includes typical physical characteristics and possible breed-level behaviours. These are written as broad tendencies using language such as "often", "may", and "commonly"—never as claims about the individual dog. A clear reminder explains that breed cannot predict this dog's personality, emotional state, safety, or future behaviour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we can see&lt;/strong&gt; — observable cues such as posture, ears, tail visibility and body position, each with a visual-confidence level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this might mean&lt;/strong&gt; — cautious interpretations of those signals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we can't know&lt;/strong&gt; — because one photograph cannot tell us everything about a dog's emotional state or predict what it will do next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you can do&lt;/strong&gt; — conservative guidance focused on giving unfamiliar dogs appropriate space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safety note&lt;/strong&gt; — an explicit reminder not to approach an unfamiliar dog based only on an image interpretation.&lt;/p&gt;

&lt;p&gt;That last part was especially important to me.&lt;/p&gt;

&lt;p&gt;I didn't want to build an AI-powered "Is this dog safe?" detector. A photograph cannot reliably tell you that a dog is safe, friendly, or won't bite.&lt;/p&gt;

&lt;p&gt;So the Gemini integration is deliberately designed to acknowledge uncertainty, using language such as "may indicate" and "can be consistent with" rather than making definitive claims. That same principle applies to breed identification: visual similarity can suggest a breed or mix, but appearance alone cannot confirm ancestry or tell us how an individual dog will behave.&lt;/p&gt;

&lt;p&gt;That safety work does not rely only on the interface. In the server path, the browser sends the image to a same-origin &lt;strong&gt;Vercel Function&lt;/strong&gt;, so the Gemini key is never exposed to client code. The function rejects unsupported files and images larger than 5 MB before calling Gemini, requests JSON matching a schema, validates the result with Zod, and rejects responses containing certainty claims such as "definitely friendly", "won't bite" or "safe to approach". The UI validates the response again before rendering it as cards, never as model-generated HTML.&lt;/p&gt;

&lt;p&gt;The Gemini model is configured server-side with &lt;code&gt;GEMINI_MODEL&lt;/code&gt; and checked against an allowlist. The current default is &lt;strong&gt;Gemini 3.5 Flash-Lite&lt;/strong&gt;, and unsupported model IDs fall back to this default rather than being passed through to the provider. For local development, Vite loads only the server-side Gemini environment variables and mounts the same &lt;code&gt;/api/analyze-dog&lt;/code&gt; handler used in deployment. That means &lt;code&gt;npm run dev&lt;/code&gt; runs the UI and local API together without requiring the Vercel CLI or a Vercel login.&lt;/p&gt;

&lt;h3&gt;
  
  
  Private on-device fallback with Gemini Nano
&lt;/h3&gt;

&lt;p&gt;When the server reports that no &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; is configured, Pawsitive checks Chrome's built-in &lt;code&gt;LanguageModel&lt;/code&gt; API with the exact text-and-image capabilities required by the feature. If the model is ready, the selected image is analysed locally. If Chrome needs to download the model first, the interface shows download progress and provides a cancel action.&lt;/p&gt;

&lt;p&gt;The on-device session receives the same cautious system instruction as server Gemini, accepts the selected photo as an image &lt;code&gt;Blob&lt;/code&gt;, and returns JSON constrained by the same schema. That schema includes the possible breed, confidence, supporting visual evidence, typical characteristics, possible breed behaviours, body-language observations, uncertainty, and safety guidance. Its output passes through the same Zod validation and certainty checks before anything is displayed. The session is destroyed after analysis to release browser resources, and successful results are clearly labelled &lt;strong&gt;analysed privately on this device&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What amazed me about the private mode
&lt;/h3&gt;

&lt;p&gt;This became one of the most surprising parts of the whole project. I expected the private on-device mode to be a useful fallback, but I did not expect Gemini Nano to perform this well. In my testing, its observations and cautious interpretations were often remarkably close to the results produced through the API-key-powered server models.&lt;/p&gt;

&lt;p&gt;API mode:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flz1z1nm67uid0ctpf5xj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flz1z1nm67uid0ctpf5xj.png" alt=" " width="799" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Private mode:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj3vfxe1c98nwajj0lvvq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj3vfxe1c98nwajj0lvvq.png" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That feels like a small glimpse of how quickly AI is advancing: a browser can now examine an image, follow a detailed instruction, produce structured output and respect the same safety constraints—all without an API key and without sending the photo to a server. The privacy benefit is not achieved by giving up the core experience; the result remains genuinely useful.&lt;/p&gt;

&lt;p&gt;There are still differences between devices and models, so I would not call the two paths identical or treat my experiments as a formal benchmark. Even so, seeing capable multimodal AI run privately inside Chrome was a real &lt;strong&gt;wow moment&lt;/strong&gt; for me. Gemini Nano started as a fallback and ended up feeling like one of Pawsitive's most exciting features—and a sign of the possibilities that on-device AI is beginning to unlock.&lt;/p&gt;

&lt;p&gt;The fallback order is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use server Gemini when &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; is configured.&lt;/li&gt;
&lt;li&gt;Use Chrome's built-in Gemini Nano model when the key is absent and the requested multimodal session is supported.&lt;/li&gt;
&lt;li&gt;Show an honest unavailable message and retain the clearly labelled seeded demo when neither option is available.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Prerequisites for private on-device analysis
&lt;/h3&gt;

&lt;p&gt;Chrome's foundation-model APIs are a progressive enhancement rather than universal browser functionality. The current documented requirements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browser:&lt;/strong&gt; Google Chrome 148 or newer for the web Prompt API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operating system:&lt;/strong&gt; Windows 10 or 11, macOS 13 or newer, Linux, or a Chromebook Plus device. Chrome on Android, iOS, and non-Plus Chromebooks is not currently supported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; At least 22 GB free on the volume containing the Chrome profile. Chrome removes the model if available storage later drops below 10 GB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute:&lt;/strong&gt; Strictly more than 4 GB GPU VRAM, or at least 16 GB system RAM and four CPU cores for CPU execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network:&lt;/strong&gt; An unmetered connection for the initial browser-managed model download. Subsequent inference can run without a network connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User activation:&lt;/strong&gt; The first download must be started after a meaningful user interaction, which Pawsitive provides through the &lt;strong&gt;Help me understand&lt;/strong&gt; button.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23xpnsd082nbnhzifz02.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23xpnsd082nbnhzifz02.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For local testing, enable &lt;code&gt;chrome://flags/#optimization-guide-on-device-model&lt;/code&gt; and &lt;code&gt;chrome://flags/#prompt-api-for-gemini-nano&lt;/code&gt;, then fully relaunch Chrome. Download and model errors can be inspected at &lt;code&gt;chrome://on-device-internals&lt;/code&gt;. After the initial download, Chrome states that inference is local and no prompt or image data is sent to Google or another third party.&lt;/p&gt;

&lt;p&gt;The AI feature also fails independently from the rest of the product. If neither server nor on-device analysis is available, the user sees a plain explanation and can continue learning. For challenge demos, there is a clearly labelled sample-photo experience with a seeded educational result; it never pretends to be a live result for a photo the user uploaded. Images are processed only after the user presses &lt;strong&gt;Help me understand&lt;/strong&gt;, and Pawsitive does not add them to application storage. On-device analysis keeps the image local; server analysis does not store it.&lt;/p&gt;

&lt;p&gt;AI isn't the authority here. &lt;strong&gt;It is the teacher.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Learning Journey
&lt;/h2&gt;

&lt;p&gt;The product principle follows a simple progression:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand → Recognise → Respond → Build Confidence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The actual lesson content is organised into &lt;strong&gt;Understanding Dogs&lt;/strong&gt;, &lt;strong&gt;Meeting Dogs Safely&lt;/strong&gt;, and &lt;strong&gt;Thoughtful Ownership&lt;/strong&gt; modules. Users first learn about relaxed and tense body language, tail movement, barking, staring, and why individual signals shouldn't be interpreted in isolation.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc6uwsyz0bidtpjjdoovy.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc6uwsyz0bidtpjjdoovy.png" alt=" " width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They then move into realistic scenarios and make decisions for themselves.&lt;/p&gt;

&lt;p&gt;If they get something wrong, Pawsitive doesn't throw a giant red &lt;strong&gt;WRONG&lt;/strong&gt; screen at them. Pip simply tilts their head and encourages them to look at the situation again.&lt;/p&gt;

&lt;p&gt;That was intentional.&lt;/p&gt;

&lt;p&gt;Someone who is already nervous doesn't need another reason to feel like they're failing.&lt;/p&gt;

&lt;p&gt;Progress follows the same principle. Onboarding starts confidence at 20, 40, 50, 70 or 85 depending on the user's own answer. Completing a lesson or correctly answering a new scenario adds five points, while an incorrect answer has no penalty. Rewards are idempotent, so repeating an activity cannot inflate the score. The Progress screen keeps confidence separate from completion and explicitly describes it as a personal reflection, not a clinical measurement.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐕 The Owner Perspective
&lt;/h2&gt;

&lt;p&gt;The owner experience came directly from thinking about what makes dog encounters uncomfortable.&lt;/p&gt;

&lt;p&gt;A dog owner may genuinely have a friendly dog and genuinely believe nothing is wrong. But that doesn't necessarily change how the other person feels.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfcvsyna7yb0wrfi5aex.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpfcvsyna7yb0wrfi5aex.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One scenario asks an owner what they should do when they notice someone moving away from their dog.&lt;/p&gt;

&lt;p&gt;The answer isn't to follow them and explain that the dog is friendly. It's simply to create some space.&lt;/p&gt;

&lt;p&gt;That captures what I wanted Pawsitive to teach:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A safe interaction isn't just about understanding the dog. It's about understanding the human too.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The paired scenario is linked in both directions in the content model. Completing the person and owner sides unlocks the &lt;strong&gt;Perspective Shift&lt;/strong&gt; achievement, making the product's central idea visible in the progress journey rather than leaving it as marketing copy.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyugycxcnx2ml9pcksnux.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyugycxcnx2ml9pcksnux.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Safety Net
&lt;/h2&gt;

&lt;p&gt;Because Pawsitive mixes educational content, stateful rewards and model output, I did not want the demo to depend only on a happy-path click-through.&lt;/p&gt;

&lt;p&gt;The project uses &lt;strong&gt;Vitest and React Testing Library&lt;/strong&gt;. The current automated suite checks the required lesson, scenario, signal and achievement counts; unique IDs and answer keys; two-way scenario links; confidence clamping and one-time rewards; and the dog-analysis schema. It also contains regression cases for unsafe AI phrases including "definitely friendly", "won't bite", "you can approach this dog" and "safe to approach".&lt;/p&gt;

&lt;p&gt;The on-device adapter tests cover unsupported browsers, successful structured multimodal output, browser model-download progress, session cleanup, and rejection of unsafe local-model certainty. A page-level test confirms that a missing server key selects the private path without uploading the image.&lt;/p&gt;

&lt;p&gt;Before submission, the strict TypeScript check, all &lt;strong&gt;20 automated tests&lt;/strong&gt;, ESLint, and the Vite production build pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;Pawsitive started with something personal: the slightly ridiculous feeling of loving dogs while still being afraid of them.&lt;/p&gt;

&lt;p&gt;I don't expect an app to erase that fear. That wasn't the goal.&lt;/p&gt;

&lt;p&gt;I wanted to build something that says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It's okay if you're nervous. Let's understand what's happening.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe knowing what to look for makes the next encounter feel a little less unpredictable. Maybe understanding that a wagging tail doesn't always mean "hello" gives someone a little more confidence. Maybe helping owners recognise when to create some distance makes the other person feel less alone in the situation.&lt;/p&gt;

&lt;p&gt;And maybe, eventually, someone who currently crosses the street when they see a dog can simply keep walking.&lt;/p&gt;

&lt;p&gt;For someone who is afraid of dogs, that might not feel like a small thing at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏆 Prize Category: Best Use of Google AI
&lt;/h2&gt;

&lt;p&gt;I'm submitting Pawsitive for &lt;strong&gt;Best Use of Google AI&lt;/strong&gt; because Gemini is part of the core learning experience rather than simply being added as a chatbot.&lt;/p&gt;

&lt;p&gt;Through &lt;strong&gt;Understand This Dog&lt;/strong&gt;, Gemini's multimodal capabilities help users explore visible dog body-language cues while deliberately teaching the limits of what can be inferred from a single image. The app can use a securely configured server model or Chrome's built-in Gemini Nano model, with shared structured-output and safety controls across both paths.&lt;/p&gt;

&lt;p&gt;A curious dog:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw8e6oc12hw8i9dkt619o.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw8e6oc12hw8i9dkt619o.png" alt=" " width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A playful dog:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fspmo1yqb37bj5xe4sqnm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fspmo1yqb37bj5xe4sqnm.png" alt=" " width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A rare breed:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsbso2wjwudadf8a1p2ts.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsbso2wjwudadf8a1p2ts.png" alt=" " width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That uncertainty is part of the feature, not a limitation hidden from the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔮 Giving Pip a Voice with ElevenLabs
&lt;/h2&gt;

&lt;p&gt;The next piece I would love to add is a voice for Pip using &lt;strong&gt;ElevenLabs&lt;/strong&gt;. ElevenLabs is &lt;strong&gt;not part of the current weekend build&lt;/strong&gt;; this is a deliberate next step rather than a shipped integration.&lt;/p&gt;

&lt;p&gt;Right now, Pip is the animated companion reacting to the user's progress. With voice, Pip could become part of the learning journey itself.&lt;/p&gt;

&lt;p&gt;Instead of simply reading instructions, Pip could say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Okay, let's take this one slowly. You can see a dog coming towards you. What would you do?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After an answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Good thinking. Giving the dog and its owner a little more space can make this easier for everyone."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And for someone who is nervous:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"There's no rush. Let's look at what's happening first."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The interesting part would be connecting Pip's voice to the user's confidence journey, so the experience gradually feels more encouraging and energetic as their confidence grows.&lt;/p&gt;

&lt;p&gt;That brings together three pieces of the experience:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini&lt;/strong&gt; understands the visual information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ElevenLabs&lt;/strong&gt; gives Pip a natural voice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Motion for React&lt;/strong&gt; brings Pip to life.&lt;/p&gt;

&lt;p&gt;Together, they could turn Pip from an animated mascot into a genuine &lt;strong&gt;dog-confidence companion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I would keep that integration server-side, accept only a small set of approved narration IDs, and cache generated clips. That would protect the API key, control cost and prevent arbitrary text from turning the feature into an open text-to-speech proxy. Narration would remain user-initiated, optional and accompanied by the same visible text.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;There are plenty of directions Pawsitive could take, but the core idea would stay the same: gradually move people from illustrations to photographs, videos and increasingly realistic scenarios as their confidence grows.&lt;/p&gt;

&lt;p&gt;I also considered &lt;strong&gt;Snowflake&lt;/strong&gt;, but deliberately did not add it to the weekend MVP simply to increase the technology count. Pawsitive does not need a warehouse to teach a lesson or remember local progress. A future opt-in analytics layer could send anonymous events such as lesson completion, perspective switching, Gemini request success and labelled-demo usage to Snowflake—never uploaded photos, model response text or personal details. For now, keeping that out made the privacy story and the architecture simpler.&lt;/p&gt;

&lt;p&gt;For dog owners, there could eventually be more personalised guidance around their individual dogs and situations.&lt;/p&gt;

&lt;p&gt;But the weekend version is intentionally focused.&lt;/p&gt;

&lt;p&gt;Pawsitive is ultimately about closing the gap between two perspectives:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I don't know what that dog is going to do."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"My dog is friendly. Why are they scared?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a lot of space between those two thoughts.&lt;/p&gt;

&lt;p&gt;I wanted to build something that helps close it.&lt;/p&gt;

&lt;p&gt;And, perhaps selfishly, something that might one day help me stop jumping every time a dog barks. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgyv9o4271ddentm1i9yd.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgyv9o4271ddentm1i9yd.png" alt=" " width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You don't have to love dogs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;You just need to understand them.🐾 *&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>gemini</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Turning Ayurveda’s Six Rasas Into CSS Art</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Sat, 15 Aug 2026 03:16:21 +0000</pubDate>
      <link>https://dev.to/ujja/rasa-turning-ayurvedas-six-rasas-into-css-art-4go6</link>
      <guid>https://dev.to/ujja/rasa-turning-ayurvedas-six-rasas-into-css-art-4go6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, CSS Art&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;What does comfort food look like when you don't use food photography?&lt;/p&gt;

&lt;p&gt;That was the question behind &lt;strong&gt;Rasa&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rasa is an interactive culinary atlas exploring India through &lt;strong&gt;Shadrasa&lt;/strong&gt;, the six tastes described in Ayurveda:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Madhura&lt;/strong&gt; — sweet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amla&lt;/strong&gt; — sour&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lavana&lt;/strong&gt; — salty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Katu&lt;/strong&gt; — pungent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tikta&lt;/strong&gt; — bitter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kashaya&lt;/strong&gt; — astringent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to turn those six tastes into something visual rather than simply presenting them as text or cards.&lt;/p&gt;

&lt;p&gt;The result is a tongue-shaped interactive map.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frin2mx4qa5m9pq76wtni.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frin2mx4qa5m9pq76wtni.png" alt="Six guided taste receptors are distributed across the tongue" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One important clarification: the familiar "tongue taste map" is scientifically inaccurate. Taste receptors are distributed across the tongue. In Rasa, the tongue is purely a &lt;strong&gt;cultural and artistic navigation metaphor&lt;/strong&gt;, not an anatomy diagram or medical claim.&lt;/p&gt;

&lt;p&gt;The idea was to make something that felt like opening an old food journal rather than browsing a database.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CSS Art
&lt;/h2&gt;

&lt;p&gt;The tongue is probably the most obvious piece of CSS art in the project.&lt;/p&gt;

&lt;p&gt;It isn't an image.&lt;/p&gt;

&lt;p&gt;It is a layered CSS illustration with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Six selectable rasa regions&lt;/li&gt;
&lt;li&gt;A draggable, bounded CSS 3D orbit&lt;/li&gt;
&lt;li&gt;Pointer-responsive lighting&lt;/li&gt;
&lt;li&gt;Pan and zoom&lt;/li&gt;
&lt;li&gt;Keyboard orbit and navigation&lt;/li&gt;
&lt;li&gt;A dedicated control to centre the 3D view&lt;/li&gt;
&lt;li&gt;Papillae and surface texture&lt;/li&gt;
&lt;li&gt;A median sulcus&lt;/li&gt;
&lt;li&gt;Subtle veins and mucosal variation&lt;/li&gt;
&lt;li&gt;Rim lighting&lt;/li&gt;
&lt;li&gt;Subsurface colour&lt;/li&gt;
&lt;li&gt;Counter-scaled labels that remain readable while zooming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tongue can be explored almost like a little map.&lt;/p&gt;

&lt;p&gt;Select a rasa and the camera moves towards that region, revealing the corresponding field notes.&lt;/p&gt;

&lt;p&gt;The 3D effect does not use WebGL, canvas or a 3D library. JavaScript only captures pointer and keyboard input and updates a few CSS custom properties. Perspective, depth, lighting, counter-rotation and shadows remain CSS work.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1vof5ofgk44aykd3f8bn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1vof5ofgk44aykd3f8bn.png" alt="Nima, the bitterness guide" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That interaction was important to me.&lt;/p&gt;

&lt;p&gt;I didn't want the CSS art to be something you simply look at.&lt;/p&gt;

&lt;p&gt;I wanted it to become part of the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making Food Without Food Images
&lt;/h2&gt;

&lt;p&gt;The other fun constraint was food itself.&lt;/p&gt;

&lt;p&gt;Instead of using food photography, every dish has a small declarative &lt;strong&gt;plate-art recipe&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8a69r98ez652ynqr6wsi.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8a69r98ez652ynqr6wsi.png" alt="Pulihora, a South Indian dish" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The renderer combines things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vessel&lt;/li&gt;
&lt;li&gt;Food form&lt;/li&gt;
&lt;li&gt;Colours&lt;/li&gt;
&lt;li&gt;Garnishes&lt;/li&gt;
&lt;li&gt;Texture&lt;/li&gt;
&lt;li&gt;Lighting&lt;/li&gt;
&lt;li&gt;Serving temperature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The renderer supports vessels including thalis, plates, bowls, kadhais, clay pots, banana leaves and leaf cones.&lt;/p&gt;

&lt;p&gt;Food forms include rounds, stacks, curries, grains, crescents, cones, cubes and strands.&lt;/p&gt;

&lt;p&gt;CSS then adds details such as oil sheen, highlights, contact shadows, steam, heat haze, condensation and cold mist.&lt;/p&gt;

&lt;p&gt;So a dish isn't a static illustration.&lt;/p&gt;

&lt;p&gt;It is data that becomes an illustration.&lt;/p&gt;

&lt;p&gt;That was one of my favourite parts of building Rasa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six Handcrafted Taste Guides
&lt;/h2&gt;

&lt;p&gt;Each rasa is accompanied by a guide with a distinct silhouette, costume, hairstyle, expression and role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Guddi&lt;/strong&gt;, the Culinary Archivist for sweet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ami&lt;/strong&gt;, the Fermentation Botanist for sour&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neer&lt;/strong&gt;, the Coastal Navigator for salty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tara&lt;/strong&gt;, the Spice Route Chronicler for pungent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nima&lt;/strong&gt;, the Ethnobotanical Elder for bitter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jamu&lt;/strong&gt;, the Tea and Tannin Cartographer for astringent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The characters are built from nested HTML and CSS rather than generated avatars, SVG artwork or image files. Gradients, borders, pseudo-elements, clipping and transforms create their faces, clothing, hair, accessories and tools.&lt;/p&gt;

&lt;p&gt;They can greet the visitor, scout ingredients and share lore. Their mouths respond while they speak, and gestures such as walking, waving and pointing are finite rather than perpetual. This keeps the characters playful without turning the page into a wall of constant motion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making CSS Feel Less Like CSS
&lt;/h2&gt;

&lt;p&gt;A lot of the work was experimenting with the things CSS is surprisingly good at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gradients + shadows + transforms + masks + pseudo-elements + 3D transforms&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Individually, none of those are particularly exotic.&lt;/p&gt;

&lt;p&gt;Combined carefully, they can start to feel much closer to illustration.&lt;/p&gt;

&lt;p&gt;I also added a small layer of atmospheric "ingredient wind" across the site.&lt;/p&gt;

&lt;p&gt;Different rasas get their own ingredient:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jaggery for sweet&lt;/li&gt;
&lt;li&gt;Mango leaf for sour&lt;/li&gt;
&lt;li&gt;Salt crystal for salty&lt;/li&gt;
&lt;li&gt;Chilli for pungent&lt;/li&gt;
&lt;li&gt;Neem leaf for bitter&lt;/li&gt;
&lt;li&gt;Jamun for astringent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ingredients drift across the page like leaves in a breeze.&lt;/p&gt;

&lt;p&gt;It is intentionally subtle.&lt;/p&gt;

&lt;p&gt;It can also be paused, disappears in forced-colour mode, and respects reduced-motion preferences.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS Art With a Purpose
&lt;/h2&gt;

&lt;p&gt;One thing I wanted to avoid was making a collection of pretty CSS experiments that didn't actually help the user.&lt;/p&gt;

&lt;p&gt;The visual language is tied to the content.&lt;/p&gt;

&lt;p&gt;The tongue represents the six tastes.&lt;/p&gt;

&lt;p&gt;The plate art represents the food.&lt;/p&gt;

&lt;p&gt;The notebook changes atmosphere depending on the dish.&lt;/p&gt;

&lt;p&gt;Hot dishes introduce warmth, steam and heat shimmer.&lt;/p&gt;

&lt;p&gt;Chilled dishes introduce condensation, frost and cool mist.&lt;/p&gt;

&lt;p&gt;Festival dishes receive restrained gold details.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp94a5vew39p5tdy2jf2e.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp94a5vew39p5tdy2jf2e.png" alt="Festive dishes" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Street food gets a subtle field-stamp treatment.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftnkzhulskvpuyppgy5o3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftnkzhulskvpuyppgy5o3.png" alt="Street food dokhla" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The visual effects are therefore part of the storytelling rather than decoration added afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  84 Dishes Instead of Six
&lt;/h2&gt;

&lt;p&gt;The six tastes are only the entry point.&lt;/p&gt;

&lt;p&gt;Rasa contains &lt;strong&gt;84 dishes&lt;/strong&gt;, with 14 primary dishes for each rasa, covering all 28 Indian states and multiple Union Territories across eight culinary macro-regions.&lt;/p&gt;

&lt;p&gt;The dataset intentionally goes beyond the usual shorthand for Indian food.&lt;/p&gt;

&lt;p&gt;There are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everyday meals&lt;/li&gt;
&lt;li&gt;Breakfasts&lt;/li&gt;
&lt;li&gt;Snacks&lt;/li&gt;
&lt;li&gt;Street foods&lt;/li&gt;
&lt;li&gt;Festival foods&lt;/li&gt;
&lt;li&gt;Temple foods&lt;/li&gt;
&lt;li&gt;City specialities&lt;/li&gt;
&lt;li&gt;Folk and Adivasi traditions&lt;/li&gt;
&lt;li&gt;Fermented foods&lt;/li&gt;
&lt;li&gt;Coastal cuisines&lt;/li&gt;
&lt;li&gt;Mountain cuisines&lt;/li&gt;
&lt;li&gt;Sweets&lt;/li&gt;
&lt;li&gt;Drinks&lt;/li&gt;
&lt;li&gt;Pickles&lt;/li&gt;
&lt;li&gt;Breads&lt;/li&gt;
&lt;li&gt;Rice dishes&lt;/li&gt;
&lt;li&gt;Curries&lt;/li&gt;
&lt;li&gt;Preserved foods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was breadth rather than stereotype.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Side
&lt;/h2&gt;

&lt;p&gt;Rasa is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React 18&lt;/li&gt;
&lt;li&gt;TypeScript 5.7&lt;/li&gt;
&lt;li&gt;Vite 6&lt;/li&gt;
&lt;li&gt;Tailwind CSS 4&lt;/li&gt;
&lt;li&gt;Lucide React&lt;/li&gt;
&lt;li&gt;Handcrafted CSS character art&lt;/li&gt;
&lt;li&gt;Playwright&lt;/li&gt;
&lt;li&gt;axe-core&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application is data-driven.&lt;/p&gt;

&lt;p&gt;The six rasas, dishes, regions, festivals, spices and guides are represented as domain models rather than being embedded directly into components.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F52pmxjxhhdw53r802aoj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F52pmxjxhhdw53r802aoj.png" alt="Indian Regions" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That makes the CSS renderer reusable.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Dish&lt;/code&gt; can describe what something is, while its &lt;code&gt;PlateArt&lt;/code&gt; recipe describes how it should look.&lt;/p&gt;

&lt;p&gt;That separation made it much easier to keep adding dishes without creating a new component for every food.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility Still Applies to CSS Art
&lt;/h2&gt;

&lt;p&gt;One of the interesting challenges was making something visually experimental still behave like a normal application.&lt;/p&gt;

&lt;p&gt;The map is keyboard-operable.&lt;/p&gt;

&lt;p&gt;The rasa controls, field-note tabs, guide commands, notebook corners and CSS 3D orbit are keyboard-operable.&lt;/p&gt;

&lt;p&gt;Interactive controls have accessible names and 44 × 44px minimum targets.&lt;/p&gt;

&lt;p&gt;There is a skip link.&lt;/p&gt;

&lt;p&gt;Notebook page changes are announced to screen readers.&lt;/p&gt;

&lt;p&gt;The experience supports reduced motion, persistent motion controls, forced colours, 200% zoom, text-spacing overrides and 320px reflow. Native dish names carry language metadata, page and guide changes use polite status announcements, and textured-paper text now uses darker ink colours for enhanced contrast.&lt;/p&gt;

&lt;p&gt;I also added 13 Playwright accessibility and interaction journeys. They run axe with WCAG 2.0, 2.1 and 2.2 A, AA and AAA tags, exercise all six rasa states and all field-note tabs, and check keyboard selection, reduced motion, text spacing, target sizes, finite guide animation, CSS 3D keyboard orbit and notebook corner navigation.&lt;/p&gt;

&lt;p&gt;The latest automated run passes all 13 journeys with no axe violations or unresolved structural ARIA checks. Textured gradients can prevent automated tools from resolving every contrast pair, so the underlying ink-on-paper colour pairs were also reviewed directly. This still is not a claim that automation proves complete WCAG AAA conformance: manual testing remains necessary for reading order, language and pronunciation, cognitive clarity, and real assistive-technology behaviour.&lt;/p&gt;

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

&lt;p&gt;The biggest lesson was that CSS art becomes much more interesting when it has a job.&lt;/p&gt;

&lt;p&gt;It is tempting to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How can I make this look cool with CSS?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better question turned out to be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What should this visual communicate?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the answer was clear, the implementation became much easier.&lt;/p&gt;

&lt;p&gt;The tongue needed depth because it was a map.&lt;/p&gt;

&lt;p&gt;The food needed texture because it represented serving and preparation.&lt;/p&gt;

&lt;p&gt;The notebook needed page turns because the content was designed as a field journal.&lt;/p&gt;

&lt;p&gt;The current notebook has clickable outer page corners as well as conventional Previous and Next controls. Each corner uses a small rounded CSS curl: the right edge lifts subtly upward and left, while the left edge lifts upward and right. The fold, underside, highlight and shadow are all CSS, while the button underneath remains semantic and keyboard accessible.&lt;/p&gt;

&lt;p&gt;The motion needed to communicate state instead of simply moving things around.&lt;/p&gt;

&lt;p&gt;That became one of the design principles for the entire project:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Motion should explain state or create atmosphere, never prevent reading.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🍛 &lt;strong&gt;Try Rasa:&lt;/strong&gt; &lt;a href="https://rasa-mu.vercel.app/" rel="noopener noreferrer"&gt;https://rasa-mu.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source code is available on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ujjavala" rel="noopener noreferrer"&gt;
        ujjavala
      &lt;/a&gt; / &lt;a href="https://github.com/ujjavala/rasa" rel="noopener noreferrer"&gt;
        rasa
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Rasa — The Tongue Map of India&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;An interactive culinary atlas that explores India through &lt;strong&gt;Shadrasa&lt;/strong&gt;, Ayurveda's six tastes: sweet, sour, salty, pungent, bitter, and astringent.&lt;/p&gt;
&lt;p&gt;Rasa is a React experience built for the DEV Frontend Challenge themes &lt;strong&gt;CSS Art: Comfort Food&lt;/strong&gt; and &lt;strong&gt;Perfect Landing: Comfort Food&lt;/strong&gt;. It combines an editorial landing page, a zoomable CSS-art tongue map, character-led exploration, regional food history, and a page-turning field notebook containing 84 dishes from across India.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; the familiar “tongue taste map” is scientifically inaccurate. Taste receptors are distributed across the tongue. This project uses the tongue only as a cultural and artistic navigation metaphor for the six rasas; it is not an anatomy chart or medical guidance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why this project exists&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Indian food is often flattened into a small set of restaurant dishes. Rasa was created to show a wider table: home cooking, street food, temple food, festival dishes…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ujjavala/rasa" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Youtube link: &lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Ayx92pdAXPI" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;There are plenty of things I'd like to explore further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingredient and state-based search&lt;/li&gt;
&lt;li&gt;Seasonal food trails&lt;/li&gt;
&lt;li&gt;Audio pronunciation for native dish names&lt;/li&gt;
&lt;li&gt;More community-reviewed cultural sources&lt;/li&gt;
&lt;li&gt;Shareable notebook pages&lt;/li&gt;
&lt;li&gt;Deeper VoiceOver, NVDA, TalkBack and keyboard-only testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, Rasa is my little experiment in asking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How far can CSS go when you stop treating it as styling and start treating it as an illustration medium?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And perhaps more importantly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a website about comfort food make you hungry without using a single food photograph?&lt;/strong&gt; 🍚&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
      <category>react</category>
    </item>
    <item>
      <title>Designing a Landing Page That Feels Like Opening a Food Journal</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Sat, 15 Aug 2026 03:16:15 +0000</pubDate>
      <link>https://dev.to/ujja/rasa-designing-a-landing-page-that-feels-like-opening-a-food-journal-mb9</link>
      <guid>https://dev.to/ujja/rasa-designing-a-landing-page-that-feels-like-opening-a-food-journal-mb9</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, Perfect Landing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I built &lt;strong&gt;Rasa&lt;/strong&gt;, an interactive culinary atlas that explores India through the six tastes of &lt;strong&gt;Shadrasa&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;sweet, sour, salty, pungent, bitter and astringent.&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmvaaku7oppfe0wzfsw2o.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmvaaku7oppfe0wzfsw2o.png" alt="Image showing India is more than just butter chicken. Also has a rasa wheel on the right" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The starting point was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if a food atlas felt less like a database and more like opening a traveller's field journal?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That idea shaped the entire landing page.&lt;/p&gt;

&lt;p&gt;Instead of starting with a grid of dishes or a traditional restaurant-style hero, I wanted the page to feel editorial, tactile and slightly mysterious.&lt;/p&gt;

&lt;p&gt;The landing page introduces the six tastes through a CSS-built rasa mandala and then invites the visitor into the atlas.&lt;/p&gt;

&lt;p&gt;From there, the experience expands into an interactive tongue map, character-led exploration, regional food history and a page-turning culinary notebook containing 84 dishes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Landing Page Is the Beginning, Not the Destination
&lt;/h2&gt;

&lt;p&gt;I approached the hero as an invitation rather than a summary.&lt;/p&gt;

&lt;p&gt;The visitor first encounters the concept of Rasa.&lt;/p&gt;

&lt;p&gt;Then the six tastes.&lt;/p&gt;

&lt;p&gt;Then the tongue.&lt;/p&gt;

&lt;p&gt;Then the exploration.&lt;/p&gt;

&lt;p&gt;That progression was deliberate.&lt;/p&gt;

&lt;p&gt;I didn't want to put every feature into the first screen.&lt;/p&gt;

&lt;p&gt;The landing page establishes the mental model first:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rasa → taste → region → dish → story&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once that relationship is understood, the rest of the application becomes easier to navigate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Tongue?
&lt;/h2&gt;

&lt;p&gt;There is a familiar visual idea of dividing the tongue into taste regions.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ep0vtpdeyupoo18ngdq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ep0vtpdeyupoo18ngdq.png" alt="Has a 3D tongue on which personified taste guides stand" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taste receptors are distributed across the tongue, so Rasa does &lt;strong&gt;not&lt;/strong&gt; present this as an anatomy chart.&lt;/p&gt;

&lt;p&gt;Instead, I used the tongue as a visual metaphor.&lt;/p&gt;

&lt;p&gt;Six regions.&lt;/p&gt;

&lt;p&gt;Six tastes.&lt;/p&gt;

&lt;p&gt;Six entry points into a much larger culinary landscape.&lt;/p&gt;

&lt;p&gt;The tongue is interactive, zoomable and keyboard accessible. It also supports a shallow, bounded 3D orbit: visitors can drag the backdrop, use arrow keys, hold Shift for larger steps, or press Home and the dedicated centre control to reset the view.&lt;/p&gt;

&lt;p&gt;Selecting a rasa moves the camera towards that region and opens its field notes.&lt;/p&gt;

&lt;p&gt;The depth effect is still CSS-first. JavaScript captures input and updates CSS custom properties; perspective, layer separation, lighting, shadows and character depth are rendered in CSS. There is no canvas, WebGL or 3D dependency.&lt;/p&gt;

&lt;p&gt;So the hero concept eventually becomes the primary navigation mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Landing Page to Field Journal
&lt;/h2&gt;

&lt;p&gt;The biggest design decision was to avoid making the experience feel like a food catalogue.&lt;/p&gt;

&lt;p&gt;India has far too many food traditions for that approach to feel right.&lt;/p&gt;

&lt;p&gt;Rasa explores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everyday home cooking&lt;/li&gt;
&lt;li&gt;Street food&lt;/li&gt;
&lt;li&gt;Festival and temple food&lt;/li&gt;
&lt;li&gt;City specialities&lt;/li&gt;
&lt;li&gt;Folk and Adivasi traditions&lt;/li&gt;
&lt;li&gt;Fermented foods&lt;/li&gt;
&lt;li&gt;Coastal cuisines&lt;/li&gt;
&lt;li&gt;Mountain cuisines&lt;/li&gt;
&lt;li&gt;Regional ingredients and techniques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current dataset contains &lt;strong&gt;84 dishes&lt;/strong&gt; across eight culinary macro-regions, representing all 28 Indian states and multiple Union Territories.&lt;/p&gt;

&lt;p&gt;Each dish includes context around its history, ingredients, technique, occasions and regional identity.&lt;/p&gt;

&lt;p&gt;The landing page therefore needed to communicate &lt;strong&gt;exploration&lt;/strong&gt;, not simply consumption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet the Guides
&lt;/h2&gt;

&lt;p&gt;I also wanted the interface to have some personality.&lt;/p&gt;

&lt;p&gt;Each rasa has a guide with a distinct identity:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Taste&lt;/th&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Madhura · sweet&lt;/td&gt;
&lt;td&gt;Guddi&lt;/td&gt;
&lt;td&gt;Culinary Archivist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amla · sour&lt;/td&gt;
&lt;td&gt;Ami&lt;/td&gt;
&lt;td&gt;Fermentation Botanist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lavana · salty&lt;/td&gt;
&lt;td&gt;Neer&lt;/td&gt;
&lt;td&gt;Coastal Navigator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Katu · pungent&lt;/td&gt;
&lt;td&gt;Tara&lt;/td&gt;
&lt;td&gt;Spice Route Chronicler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tikta · bitter&lt;/td&gt;
&lt;td&gt;Nima&lt;/td&gt;
&lt;td&gt;Ethnobotanical Elder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kashaya · astringent&lt;/td&gt;
&lt;td&gt;Jamu&lt;/td&gt;
&lt;td&gt;Tea and Tannin Cartographer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each guide can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Greet&lt;/li&gt;
&lt;li&gt;Scout ingredients&lt;/li&gt;
&lt;li&gt;Reveal lore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The portraits are handcrafted from nested HTML and CSS. Each guide has a different figure, skin tone, costume, hairstyle, accessories and expression, with Guddi, Tara and Nima represented as women and Ami, Neer and Jamu as men. They use no generated avatar package, SVG character asset or image file.&lt;/p&gt;

&lt;p&gt;Their walking, waving, scouting and lore gestures are intentionally finite rather than constant idle animation. Speech bubbles restart for each command, and the mouth moves briefly while a guide talks.&lt;/p&gt;

&lt;p&gt;That was another deliberate choice.&lt;/p&gt;

&lt;p&gt;I wanted characters to feel responsive rather than noisy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Notebook
&lt;/h2&gt;

&lt;p&gt;The deeper you go into the experience, the more the interface changes.&lt;/p&gt;

&lt;p&gt;Instead of a conventional card grid, dishes appear in a two-page field notebook.&lt;/p&gt;

&lt;p&gt;You can turn pages.&lt;/p&gt;

&lt;p&gt;You can also use the lifted outer corners: the right corner turns forward and the left corner turns backward. They are semantic buttons with subtle rounded CSS curls that rise inward like the edge of a real page, while the existing Previous, Next and direct-jump controls remain available.&lt;/p&gt;

&lt;p&gt;Jump directly to a dish.&lt;/p&gt;

&lt;p&gt;Move backwards and forwards.&lt;/p&gt;

&lt;p&gt;See regional and contextual annotations.&lt;/p&gt;

&lt;p&gt;Read the native name with appropriate language metadata.&lt;/p&gt;

&lt;p&gt;Follow the dish to further context.&lt;/p&gt;

&lt;p&gt;The notebook also changes atmosphere based on the food.&lt;/p&gt;

&lt;p&gt;A piping-hot dish can introduce warmth, steam and heat shimmer.&lt;/p&gt;

&lt;p&gt;A chilled dish can introduce frost, condensation and cool mist.&lt;/p&gt;

&lt;p&gt;Festival foods receive restrained gold detailing.&lt;/p&gt;

&lt;p&gt;Street foods receive a subtle field-stamp treatment.&lt;/p&gt;

&lt;p&gt;This was my way of making the interface feel like a physical object rather than another collection of UI cards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing for Accessibility From the Start
&lt;/h2&gt;

&lt;p&gt;A visually heavy landing page can very quickly become inaccessible.&lt;/p&gt;

&lt;p&gt;So accessibility wasn't treated as something to check after the animations were finished.&lt;/p&gt;

&lt;p&gt;The application includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semantic landmarks and headings&lt;/li&gt;
&lt;li&gt;A skip link&lt;/li&gt;
&lt;li&gt;Keyboard-operable navigation&lt;/li&gt;
&lt;li&gt;Roving tab navigation&lt;/li&gt;
&lt;li&gt;Visible focus states&lt;/li&gt;
&lt;li&gt;Accessible icon controls&lt;/li&gt;
&lt;li&gt;44 × 44px minimum interactive targets&lt;/li&gt;
&lt;li&gt;Screen-reader announcements&lt;/li&gt;
&lt;li&gt;Reduced-motion support&lt;/li&gt;
&lt;li&gt;Persistent pause/play motion control&lt;/li&gt;
&lt;li&gt;Forced-colour support&lt;/li&gt;
&lt;li&gt;200% zoom support&lt;/li&gt;
&lt;li&gt;320px reflow&lt;/li&gt;
&lt;li&gt;Text-spacing resilience&lt;/li&gt;
&lt;li&gt;Keyboard control for the CSS 3D orbit and notebook corners&lt;/li&gt;
&lt;li&gt;Enhanced ink-on-paper contrast&lt;/li&gt;
&lt;li&gt;Automated axe checks through Playwright across all six rasa states and every field-note tab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The principle was simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The experience should remain useful when the visual effects disappear.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If someone disables animation, uses keyboard navigation, zooms the page or doesn't have pointer input, the content and navigation still need to make sense.&lt;/p&gt;

&lt;p&gt;The current suite contains 13 accessibility and interaction journeys and passes in Chromium. It runs axe using WCAG 2.0, 2.1 and 2.2 A, AA and AAA tags, and now rejects unresolved structural ARIA findings as well as violations. Automation still cannot certify every AAA success criterion, especially where manual judgement, language, pronunciation, reading clarity or real assistive technology is involved, so manual review remains part of the roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motion With Meaning
&lt;/h2&gt;

&lt;p&gt;There is quite a lot of movement in Rasa.&lt;/p&gt;

&lt;p&gt;But every type of movement has a reason.&lt;/p&gt;

&lt;p&gt;Guide movement communicates an interaction.&lt;/p&gt;

&lt;p&gt;The camera movement communicates navigation.&lt;/p&gt;

&lt;p&gt;Notebook movement communicates page direction.&lt;/p&gt;

&lt;p&gt;Steam and heat haze communicate serving temperature.&lt;/p&gt;

&lt;p&gt;Ingredient wind creates atmosphere.&lt;/p&gt;

&lt;p&gt;And the global motion control lets the user pause the experience.&lt;/p&gt;

&lt;p&gt;The application also respects &lt;code&gt;prefers-reduced-motion&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This led to one of my favourite principles from the project:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Movement should explain state or create atmosphere, never prevent reading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Visual Language
&lt;/h2&gt;

&lt;p&gt;I wanted the interface to feel somewhere between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a field journal + an old map + a recipe notebook + a museum exhibit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Brass.&lt;/p&gt;

&lt;p&gt;Paper.&lt;/p&gt;

&lt;p&gt;Ink.&lt;/p&gt;

&lt;p&gt;Food vessels.&lt;/p&gt;

&lt;p&gt;Spice textures.&lt;/p&gt;

&lt;p&gt;Map controls.&lt;/p&gt;

&lt;p&gt;Shadows.&lt;/p&gt;

&lt;p&gt;Atmospheric movement.&lt;/p&gt;

&lt;p&gt;Most of those visual elements are created with CSS rather than remote food photography.&lt;/p&gt;

&lt;p&gt;That constraint ended up becoming one of the most interesting parts of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Stack
&lt;/h2&gt;

&lt;p&gt;The application is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React 18&lt;/li&gt;
&lt;li&gt;TypeScript 5.7 in strict mode&lt;/li&gt;
&lt;li&gt;Vite 6&lt;/li&gt;
&lt;li&gt;Tailwind CSS 4&lt;/li&gt;
&lt;li&gt;Lucide React&lt;/li&gt;
&lt;li&gt;Handcrafted HTML and CSS character art&lt;/li&gt;
&lt;li&gt;ESLint&lt;/li&gt;
&lt;li&gt;Stylelint&lt;/li&gt;
&lt;li&gt;Playwright&lt;/li&gt;
&lt;li&gt;axe-core&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application is deliberately data-driven.&lt;/p&gt;

&lt;p&gt;Rasas, dishes, regions, festivals, spices and explorers are represented as data models.&lt;/p&gt;

&lt;p&gt;React components are responsible for rendering those models rather than containing the cultural content themselves.&lt;/p&gt;

&lt;p&gt;That made it possible to build a fairly elaborate experience without turning the components into giant collections of hard-coded content.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Enjoyed Most
&lt;/h2&gt;

&lt;p&gt;The most satisfying part was seeing a landing-page idea evolve into an actual world.&lt;/p&gt;

&lt;p&gt;At first it was just:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;six tastes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;six tastes → six regions → six guides.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;guides → dishes → regions → festivals → spices.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Eventually it became an atlas containing 84 dishes and multiple ways to explore them.&lt;/p&gt;

&lt;p&gt;That progression made the project feel much more like a small product than a challenge submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Improve Next
&lt;/h2&gt;

&lt;p&gt;There is still plenty I would like to add.&lt;/p&gt;

&lt;p&gt;Some ideas are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search by ingredient, state, season and technique&lt;/li&gt;
&lt;li&gt;Audio pronunciation for native dish names&lt;/li&gt;
&lt;li&gt;User-created food trails&lt;/li&gt;
&lt;li&gt;Shareable notebook pages&lt;/li&gt;
&lt;li&gt;More community-reviewed citations and oral-history sources&lt;/li&gt;
&lt;li&gt;More manual accessibility testing with VoiceOver, NVDA and TalkBack&lt;/li&gt;
&lt;li&gt;Additional contrast review for complex textured and gradient surfaces&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🍛 &lt;strong&gt;Explore Rasa:&lt;/strong&gt; &lt;a href="https://rasa-mu.vercel.app/" rel="noopener noreferrer"&gt;https://rasa-mu.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;The project is open source on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ujjavala" rel="noopener noreferrer"&gt;
        ujjavala
      &lt;/a&gt; / &lt;a href="https://github.com/ujjavala/rasa" rel="noopener noreferrer"&gt;
        rasa
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Rasa — The Tongue Map of India&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;An interactive culinary atlas that explores India through &lt;strong&gt;Shadrasa&lt;/strong&gt;, Ayurveda's six tastes: sweet, sour, salty, pungent, bitter, and astringent.&lt;/p&gt;
&lt;p&gt;Rasa is a React experience built for the DEV Frontend Challenge themes &lt;strong&gt;CSS Art: Comfort Food&lt;/strong&gt; and &lt;strong&gt;Perfect Landing: Comfort Food&lt;/strong&gt;. It combines an editorial landing page, a zoomable CSS-art tongue map, character-led exploration, regional food history, and a page-turning field notebook containing 84 dishes from across India.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; the familiar “tongue taste map” is scientifically inaccurate. Taste receptors are distributed across the tongue. This project uses the tongue only as a cultural and artistic navigation metaphor for the six rasas; it is not an anatomy chart or medical guidance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why this project exists&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Indian food is often flattened into a small set of restaurant dishes. Rasa was created to show a wider table: home cooking, street food, temple food, festival dishes…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ujjavala/rasa" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Youtube link:   &lt;iframe src="https://www.youtube.com/embed/Ayx92pdAXPI"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;I started with a comfort-food challenge.&lt;/p&gt;

&lt;p&gt;I ended up building a small interactive atlas.&lt;/p&gt;

&lt;p&gt;That was probably the most enjoyable part of the challenge.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;"How do I make a landing page about food?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I asked:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How would a food atlas feel if you could walk into it?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rasa is my attempt at that answer.&lt;/p&gt;

&lt;p&gt;A little paper.&lt;/p&gt;

&lt;p&gt;A little spice.&lt;/p&gt;

&lt;p&gt;A little CSS magic.&lt;/p&gt;

&lt;p&gt;And a very large table. 🍛&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>You Know What's Worse Than Not Being Able to Log In?</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Thu, 13 Aug 2026 12:38:44 +0000</pubDate>
      <link>https://dev.to/ujja/you-know-whats-worse-than-not-being-able-to-log-in-5379</link>
      <guid>https://dev.to/ujja/you-know-whats-worse-than-not-being-able-to-log-in-5379</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You Know What's Worse Than Not Being Able to Log In?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Being told everything worked right up until you try to actually use your account.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, that was a real bug.&lt;/p&gt;

&lt;p&gt;And, somehow, I ended up being pulled into another authentication mystery. At this point, I’m starting to think authentication bugs have a personal grudge against me. 😅&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/ujja/you-know-what-happens-when-theres-a-bug-in-authentication-absolutely-nothing-g0m"&gt;previous Smash Story&lt;/a&gt;, I wrote about a bug where users simply couldn't log in. This time, the problem was sneakier because most of the flow looked completely healthy. The user was approved, the background task ran, the email and SMS arrived, and Cognito had a user.&lt;/p&gt;

&lt;p&gt;Then the user actually tried to use their account.&lt;/p&gt;

&lt;p&gt;And everything fell apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Started With Two User Pools
&lt;/h2&gt;

&lt;p&gt;The authentication setup was fairly large and had evolved over time, so there wasn't one shiny User Pool doing everything.&lt;/p&gt;

&lt;p&gt;We had an older Cognito User Pool supporting existing authentication flows, including mobile-based signup, while a newer User Pool handled a newer flow where users received an email containing their PIN. Both pools were intentional because they supported different parts of the authentication journey.&lt;/p&gt;

&lt;p&gt;That wasn't the problem.&lt;/p&gt;

&lt;p&gt;The interesting part was that the application database had its own representation of a user, while Cognito had another. On top of that, some of the work connecting those two systems happened asynchronously.&lt;/p&gt;

&lt;p&gt;As long as everyone agreed about who the user was, nobody cared.&lt;/p&gt;

&lt;p&gt;The moment they disagreed, authentication became very interested.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tiny Timing Window
&lt;/h2&gt;

&lt;p&gt;The problem appeared in the partner and dependant journey.&lt;/p&gt;

&lt;p&gt;A member could create a partner or dependant during signup or later from the member details area. A relevant non-member user would then approve the account, which scheduled an asynchronous task called &lt;code&gt;SendingEmailsAfterApprovalBot&lt;/code&gt; in a &lt;code&gt;TaskList&lt;/code&gt; database table.&lt;/p&gt;

&lt;p&gt;That task ran every 15 minutes, and once it executed, the partner or dependant received an email and SMS containing a code. They could then confirm their account, set a password and sign in.&lt;/p&gt;

&lt;p&gt;Sounds reasonable, right?&lt;/p&gt;

&lt;p&gt;Now imagine this.&lt;/p&gt;

&lt;p&gt;A partner gets approved at 10:01 AM, so the background task is scheduled and everyone moves on with their lives. At 10:05 AM, before the task has run, the partner changes their name.&lt;/p&gt;

&lt;p&gt;That tiny change matters because the username for partner and dependant accounts is derived from their details. The database now has the &lt;strong&gt;new username&lt;/strong&gt;, while Cognito may still know about the &lt;strong&gt;old username&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nothing has visibly broken yet.&lt;/p&gt;

&lt;p&gt;The task is still waiting patiently in the database, completely unaware that the identity it is about to process has changed underneath it.&lt;/p&gt;

&lt;p&gt;Then, 15 minutes later, it wakes up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cognito Says, "I've Seen This Email Before"
&lt;/h2&gt;

&lt;p&gt;The background task tries to create the Cognito user using the latest username stored in the database.&lt;/p&gt;

&lt;p&gt;Cognito rejects the creation because the email address is already associated with an existing user, but that user has a different username.&lt;/p&gt;

&lt;p&gt;And there is an important detail here: &lt;strong&gt;the Cognito username is immutable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So we weren't dealing with a simple "just update the username" situation. We had two systems describing the same person with different usernames, and Cognito was quite reasonably refusing to create another user with the same email.&lt;/p&gt;

&lt;p&gt;The database was saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This is the current username for this person."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cognito was effectively saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I already know this email, and it belongs to someone with another username."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same human.&lt;/p&gt;

&lt;p&gt;Different digital identity.&lt;/p&gt;

&lt;p&gt;And that mismatch was enough to break the journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything Looked Fine... Until It Didn't
&lt;/h2&gt;

&lt;p&gt;What made this bug particularly sneaky was what happened next.&lt;/p&gt;

&lt;p&gt;The user could still receive the expected email and SMS. There was no obvious failure screaming for attention. The asynchronous task had run, notifications had gone out and Cognito already had a user associated with the email.&lt;/p&gt;

&lt;p&gt;So the user did what any reasonable person would do: they followed the instructions.&lt;/p&gt;

&lt;p&gt;They entered their code, tried to confirm their account and attempted to set their password.&lt;/p&gt;

&lt;p&gt;Except the identity represented in Cognito didn't match the latest identity represented in the database.&lt;/p&gt;

&lt;p&gt;The user wasn't looking at a database synchronisation problem. They weren't thinking about immutable Cognito usernames or asynchronous jobs. They had received a perfectly legitimate code and were simply trying to finish creating their account.&lt;/p&gt;

&lt;p&gt;From their perspective, the system had basically said:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Here's your code. Everything is ready!"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Actually... never mind."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That was the bug we had to chase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Sentry
&lt;/h2&gt;

&lt;p&gt;By this point, we knew something was going wrong somewhere between the database, the background task and Cognito, but knowing &lt;em&gt;where&lt;/em&gt; it broke was another story.&lt;/p&gt;

&lt;p&gt;This is where Sentry became extremely useful. The error context gave us the details around the Cognito failure, while the breadcrumbs helped reconstruct the events leading up to it. Instead of seeing a Cognito error in isolation and assuming we simply had a duplicate-user problem, we could look at what happened before the failure and connect it back to the user's journey.&lt;/p&gt;

&lt;p&gt;The important clue was that Cognito was rejecting the user creation because the email was already associated with an existing user. That told us &lt;em&gt;what&lt;/em&gt; Cognito didn't like, but the surrounding context helped explain &lt;em&gt;why&lt;/em&gt; we had reached that point in the first place.&lt;/p&gt;

&lt;p&gt;We could see the relevant user and operation context around the failure, follow the activity leading up to the Cognito call, and use the correlation ID to connect the same operation across services and logs. That made it much easier to piece together the timeline rather than chasing individual log entries and hoping they belonged to the same request.&lt;/p&gt;

&lt;p&gt;And once the timeline was visible, the problem suddenly made sense:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;approval → delayed task → name changed → username changed → Cognito already has the email → creation rejected → identities drift apart → confirmation fails&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That was the moment the bug stopped looking mysterious.&lt;/p&gt;

&lt;p&gt;The code told us what &lt;em&gt;should&lt;/em&gt; happen. Sentry's error context, breadcrumbs and correlated logs helped us see what &lt;em&gt;actually&lt;/em&gt; happened.&lt;/p&gt;

&lt;p&gt;And honestly, that's one of the best parts of having good observability during an authentication investigation. The error tells you where the system complained; the breadcrumbs help you understand the story that led it there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix Was Simpler Than the Investigation
&lt;/h2&gt;

&lt;p&gt;Once we understood the problem, the fix was surprisingly straightforward.&lt;/p&gt;

&lt;p&gt;We already checked whether a Cognito user existed for the email, but we were treating that as enough. Instead, we changed the logic to ask: &lt;em&gt;"Does the correct Cognito user for the current database record exist?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the email existed in Cognito but belonged to an outdated username, we deleted that user and recreated it with the latest username from the database. Since Cognito usernames are immutable, recreating the user was necessary to bring the two systems back into alignment.&lt;/p&gt;

&lt;p&gt;The important change wasn't the delete-and-recreate itself. It was recognising that &lt;strong&gt;the database was the source of truth for the current username&lt;/strong&gt;, while Cognito could still be holding an older representation of the same person.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Weird Scenario Was the Important One
&lt;/h2&gt;

&lt;p&gt;The happy path already worked, so the interesting tests were the ones where state changed between steps.&lt;/p&gt;

&lt;p&gt;We tested the normal signup flows, partner and dependant creation, existing Cognito users, missing Cognito users and, most importantly, the scenario that exposed the bug: a partner or dependant was approved, changed their name before &lt;code&gt;SendingEmailsAfterApprovalBot&lt;/code&gt; ran, and then the background process tried to create their Cognito user using the latest database state.&lt;/p&gt;

&lt;p&gt;We then verified the part that actually mattered: could they receive their PIN, confirm their account, set their password and successfully sign in?&lt;/p&gt;

&lt;p&gt;Because nobody cares about our beautifully synchronised identity records. They just want to get into their account.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Interesting Part Wasn't Actually Cognito
&lt;/h2&gt;

&lt;p&gt;Looking back, this wasn't really a Cognito bug. The database wasn't broken, and neither was the scheduled task. Each component was behaving according to its own rules.&lt;/p&gt;

&lt;p&gt;The problem was the assumption connecting them: that the identity we started processing would still be the identity we needed when the asynchronous process eventually ran.&lt;/p&gt;

&lt;p&gt;Then someone changed their name.&lt;/p&gt;

&lt;p&gt;That small change was enough to make the database username different from the immutable username already held by Cognito. Since the email was already taken, Cognito rejected the new user creation and the two systems drifted apart.&lt;/p&gt;

&lt;p&gt;That's the lesson I took from it: whenever identity exists in multiple systems, you need to be explicit about &lt;strong&gt;which system is the source of truth&lt;/strong&gt; and what happens when those representations disagree.&lt;/p&gt;

&lt;p&gt;Because eventually, they will.&lt;/p&gt;

&lt;p&gt;Someone will change their name. A background job will run later. A legacy system will already have a record. And Cognito will quite reasonably tell you that it has seen that email before.&lt;/p&gt;

&lt;h2&gt;
  
  
  And That's What Made This Bug So Sneaky
&lt;/h2&gt;

&lt;p&gt;The approval worked, the task ran, the notification was sent, Cognito had a user and the user followed the instructions. Yet somewhere between those perfectly reasonable steps, the identity had drifted.&lt;/p&gt;

&lt;p&gt;From the user's perspective, none of the underlying details mattered. They didn't know there were two User Pools, that the old pool handled mobile signup while the newer one sent the email PIN, or that a background task ran every 15 minutes.&lt;/p&gt;

&lt;p&gt;They simply knew they had done everything they were asked to do and still couldn't get into their account.&lt;/p&gt;

&lt;p&gt;Which brings me back to the opening question.&lt;/p&gt;

&lt;p&gt;What's worse than not being able to log in?&lt;/p&gt;

&lt;p&gt;Being told everything worked right up until you try to actually use your account.&lt;/p&gt;

&lt;p&gt;At least a failed login is honest. This bug was more like:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Congratulations! Everything worked!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Really? Because I'm still locked out."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And that's probably the biggest lesson I took away from it: when debugging authentication, don't just look at the login screen. Follow the identity through the entire system, use your observability tools to reconstruct what actually happened, and find the moment when the different pieces stopped agreeing about who that user was.&lt;/p&gt;

&lt;p&gt;Because sometimes the authentication bug isn't hiding at login.&lt;/p&gt;

&lt;p&gt;Sometimes it's hiding in a background job, quietly waiting for someone to change their name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auth strikes again. 🔐🐛&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>distributedsystems</category>
      <category>webdev</category>
    </item>
    <item>
      <title>You Know What Happens When There's a Bug in Authentication? Absolutely Nothing.</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Thu, 30 Jul 2026 14:23:19 +0000</pubDate>
      <link>https://dev.to/ujja/you-know-what-happens-when-theres-a-bug-in-authentication-absolutely-nothing-g0m</link>
      <guid>https://dev.to/ujja/you-know-what-happens-when-theres-a-bug-in-authentication-absolutely-nothing-g0m</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything Stops
&lt;/h2&gt;

&lt;p&gt;You know what happens when there's a bug in authentication?&lt;/p&gt;

&lt;p&gt;Nothing.&lt;/p&gt;

&lt;p&gt;Seriously... absolutely nothing.&lt;/p&gt;

&lt;p&gt;Not because nothing is broken, but because users simply can't get past the front door. They can't log in, which means they can't access the application, complete a booking, view their information, or do whatever they came there to do in the first place.&lt;/p&gt;

&lt;p&gt;Normally that's annoying.&lt;/p&gt;

&lt;p&gt;But what if the application isn't just another website?&lt;/p&gt;

&lt;p&gt;At the time, I was part of the authentication engineering team responsible for rolling out Multi-Factor Authentication (MFA) for government service centres across the state. These weren't internal dashboards or social media apps. These were systems used every day by frontline staff helping thousands of citizens.&lt;/p&gt;

&lt;p&gt;No login meant staff couldn't process driver's licence applications or renewals. They couldn't register births, deaths or marriages. They couldn't update identity records. During emergencies, they couldn't access systems used to support disaster relief efforts.&lt;/p&gt;

&lt;p&gt;The application itself was perfectly healthy. The backend was running. APIs were responding. Databases were online.&lt;/p&gt;

&lt;p&gt;But none of that mattered.&lt;/p&gt;

&lt;p&gt;Authentication is the front door. If the front door doesn't open, it doesn't matter how well the rest of the house is built.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Rollout We Thought Was Perfect
&lt;/h2&gt;

&lt;p&gt;Because authentication is so critical, we treated the rollout with the level of care you'd expect.&lt;/p&gt;

&lt;p&gt;We had unit tests, integration tests, end-to-end tests, security reviews and user acceptance testing. We tested happy paths, failure scenarios, edge cases and everything in between.&lt;/p&gt;

&lt;p&gt;Everything passed.&lt;/p&gt;

&lt;p&gt;The rollout went live.&lt;/p&gt;

&lt;p&gt;For a while, everything looked great.&lt;/p&gt;

&lt;p&gt;Then a few support tickets started appearing.&lt;/p&gt;

&lt;p&gt;Some users couldn't log in.&lt;/p&gt;

&lt;p&gt;The strange part was that it only affected a handful of people. Most users authenticated successfully, while a small group consistently received a server error.&lt;/p&gt;

&lt;p&gt;Those are the bugs every engineer dreads.&lt;/p&gt;

&lt;p&gt;If nobody can log in, you usually know where to start.&lt;/p&gt;

&lt;p&gt;If only &lt;em&gt;some&lt;/em&gt; users can't log in, every assumption suddenly becomes a possibility.&lt;/p&gt;

&lt;p&gt;Was it their browser?&lt;/p&gt;

&lt;p&gt;Their location?&lt;/p&gt;

&lt;p&gt;Their account?&lt;/p&gt;

&lt;p&gt;A feature flag?&lt;/p&gt;

&lt;p&gt;Some weird cached session?&lt;/p&gt;

&lt;p&gt;We checked everything.&lt;/p&gt;

&lt;p&gt;We dug through authentication logs, replayed requests, inspected network traffic, reviewed configuration, and read the login code more times than I'd like to admit.&lt;/p&gt;

&lt;p&gt;Everything looked correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop Looking at Logs. Watch People.
&lt;/h2&gt;

&lt;p&gt;Eventually, our team decided to do something different.&lt;/p&gt;

&lt;p&gt;Instead of staring at dashboards and log files, we visited one of the service centres and watched people use the application.&lt;/p&gt;

&lt;p&gt;Within minutes, we noticed something.&lt;/p&gt;

&lt;p&gt;The users clicking the &lt;strong&gt;Login&lt;/strong&gt; button with their mouse authenticated successfully every single time.&lt;/p&gt;

&lt;p&gt;The users navigating with the keyboard, pressing &lt;strong&gt;Tab&lt;/strong&gt; and then &lt;strong&gt;Enter&lt;/strong&gt;, consistently hit the server error.&lt;/p&gt;

&lt;p&gt;That tiny observation immediately felt important.&lt;/p&gt;

&lt;p&gt;As the feature lead developer for the MFA rollout, something clicked.&lt;/p&gt;

&lt;p&gt;This wasn't random.&lt;/p&gt;

&lt;p&gt;This wasn't infrastructure.&lt;/p&gt;

&lt;p&gt;This wasn't the backend.&lt;/p&gt;

&lt;p&gt;It had to be the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Smallest Bug with the Biggest Impact
&lt;/h2&gt;

&lt;p&gt;The culprit?&lt;/p&gt;

&lt;p&gt;One missing line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For anyone unfamiliar with it, &lt;code&gt;e.preventDefault()&lt;/code&gt; prevents the browser from performing its default action.&lt;/p&gt;

&lt;p&gt;In a React Single Page Application, forgetting it during form submission means the browser performs a traditional form submission instead of letting React handle the authentication flow.&lt;/p&gt;

&lt;p&gt;Our React application was being bypassed completely.&lt;/p&gt;

&lt;p&gt;Instead of executing our login logic, the browser submitted the form directly to the backend.&lt;/p&gt;

&lt;p&gt;Mouse clicks happened to work through our React event handler.&lt;/p&gt;

&lt;p&gt;Keyboard submissions didn't.&lt;/p&gt;

&lt;p&gt;Every automated test had passed.&lt;/p&gt;

&lt;p&gt;Every security review had passed.&lt;/p&gt;

&lt;p&gt;And yet government service centre staff couldn't access the systems they needed simply because one line of code was missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson Wasn't About &lt;code&gt;e.preventDefault()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I'm proud that once I saw someone press &lt;strong&gt;Enter&lt;/strong&gt;, I immediately suspected browser default behaviour and tracked the problem back to the missing &lt;code&gt;e.preventDefault()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But honestly, that wasn't the biggest lesson.&lt;/p&gt;

&lt;p&gt;The biggest lesson was about users.&lt;/p&gt;

&lt;p&gt;We spend a lot of time talking about AI-assisted development, automated testing and improving engineering productivity.&lt;/p&gt;

&lt;p&gt;I'm a huge believer in all of them.&lt;/p&gt;

&lt;p&gt;AI helps me write better code, generate tests, review pull requests and explore solutions much faster than I could a few years ago.&lt;/p&gt;

&lt;p&gt;But AI only validates the scenarios we think to test.&lt;/p&gt;

&lt;p&gt;Real users don't.&lt;/p&gt;

&lt;p&gt;Some never touch a mouse.&lt;/p&gt;

&lt;p&gt;Some navigate entirely with a keyboard.&lt;/p&gt;

&lt;p&gt;Some use assistive technologies.&lt;/p&gt;

&lt;p&gt;Some interact with software in ways we'd never think of while sitting at our desks.&lt;/p&gt;

&lt;p&gt;No amount of automation can replace watching someone actually use your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Whenever people ask me about memorable production bugs, I don't think about distributed systems failures or obscure race conditions.&lt;/p&gt;

&lt;p&gt;I think about a missing &lt;code&gt;e.preventDefault()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One line of code.&lt;/p&gt;

&lt;p&gt;A rollout that had passed every test.&lt;/p&gt;

&lt;p&gt;And government service centres where staff couldn't help citizens because someone pressed &lt;strong&gt;Enter&lt;/strong&gt; instead of clicking a button.&lt;/p&gt;

&lt;p&gt;Sometimes the smallest bugs have the biggest impact.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Passion Atlas: A Living Map of Human Curiosity</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Sun, 12 Jul 2026 15:32:47 +0000</pubDate>
      <link>https://dev.to/ujja/passion-atlas-a-living-map-of-human-curiosity-298h</link>
      <guid>https://dev.to/ujja/passion-atlas-a-living-map-of-human-curiosity-298h</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I built &lt;strong&gt;Passion Atlas&lt;/strong&gt; — an AI-powered map of human curiosity that helps people discover how their interests, experiences, cultures, and ideas connect.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhomudh9u0tty214chkuy.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhomudh9u0tty214chkuy.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea started from something I noticed about myself: I have never had a single passion.&lt;/p&gt;

&lt;p&gt;Throughout my life, my interests kept evolving — from anime to dance, boxing to travel, food, culture, and understanding how people live around the world. For a long time, I thought this meant I lacked focus.&lt;/p&gt;

&lt;p&gt;But I eventually realised my interests were not random; they were connected.&lt;/p&gt;

&lt;p&gt;Anime introduced me to storytelling and different cultures. Dance connected me to movement and expression. Boxing taught me discipline and human psychology. Travel connected everything through food, history, traditions, and people's stories.&lt;/p&gt;

&lt;p&gt;Maybe humans are not designed around one fixed passion. Maybe curiosity itself is the passion.&lt;/p&gt;

&lt;p&gt;Our minds naturally explore, combine, and connect ideas. The most meaningful discoveries often happen when seemingly unrelated interests come together.&lt;/p&gt;

&lt;p&gt;Passion Atlas was built around this idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your interests are not separate islands. They are a connected universe.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Passion Atlas
&lt;/h2&gt;

&lt;p&gt;Passion Atlas creates a personal &lt;strong&gt;Passion Genome&lt;/strong&gt; — a living representation of someone's curiosity.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6h6qv5rfblshm7wdn0c.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6h6qv5rfblshm7wdn0c.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;"What is your passion?"&lt;/p&gt;

&lt;p&gt;It asks:&lt;/p&gt;

&lt;p&gt;"What are all the things that make you curious, and how do they connect?"&lt;/p&gt;

&lt;p&gt;A user might enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Travel
Food
Photography
History
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Instead of generating a generic recommendation list, Passion Atlas creates connections:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Travel

↓

Cultural Exchange

↓

Local Food Traditions

↓

Storytelling

↓

Heritage Preservation

↓

Traditional Crafts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The goal is not to tell people what they should like.&lt;/p&gt;

&lt;p&gt;The goal is to reveal connections they never noticed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Passion Genome: Your Curiosity Fingerprint
&lt;/h2&gt;

&lt;p&gt;Every person has a unique combination of experiences.&lt;/p&gt;

&lt;p&gt;Two people can love the same thing but have completely different curiosity paths.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Person A:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Photography
+
Travel
+
History

=

Cultural Storytelling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Person B:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Photography
+
Nature
+
Science

=

Environmental Documentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The same interest can evolve into completely different worlds.&lt;/p&gt;

&lt;p&gt;Passion Atlas captures these unique intersections.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Passion Butterfly Effect
&lt;/h2&gt;

&lt;p&gt;Small moments can create unexpected journeys.&lt;/p&gt;

&lt;p&gt;A single experience:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I attended a traditional tea ceremony in Japan."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Could become:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tea Culture

↓

Japanese Aesthetics

↓

Ceramics

↓

Traditional Crafts

↓

Sustainable Design
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A small spark creates a larger curiosity path.&lt;/p&gt;

&lt;p&gt;Passion Atlas helps reveal these hidden chains.&lt;/p&gt;
&lt;h2&gt;
  
  
  Curiosity Wormholes
&lt;/h2&gt;

&lt;p&gt;Most recommendation systems optimise for similarity.&lt;/p&gt;

&lt;p&gt;Passion Atlas optimises for meaningful surprise.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgd3w3vrzxeuiako6xka6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgd3w3vrzxeuiako6xka6.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;"People who like hiking also like camping."&lt;/p&gt;

&lt;p&gt;It explores:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hiking

↓

Mountain Ecosystems

↓

Indigenous Knowledge

↓

Ancient Navigation

↓

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

&lt;/div&gt;


&lt;p&gt;Because sometimes the best discovery is something you were never searching for.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preserving Human Stories
&lt;/h2&gt;

&lt;p&gt;Passion is not only about consuming knowledge.&lt;/p&gt;

&lt;p&gt;It is also about preserving it.&lt;/p&gt;

&lt;p&gt;Across the world, thousands of traditions, recipes, crafts, and personal stories risk disappearing.&lt;/p&gt;

&lt;p&gt;Passion Atlas creates &lt;strong&gt;Passion Time Capsules&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff5unobk9mah6ec9qwx7h.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff5unobk9mah6ec9qwx7h.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;People can contribute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personal stories&lt;/li&gt;
&lt;li&gt;cultural memories&lt;/li&gt;
&lt;li&gt;recipes&lt;/li&gt;
&lt;li&gt;artisan knowledge&lt;/li&gt;
&lt;li&gt;images&lt;/li&gt;
&lt;li&gt;audio recordings&lt;/li&gt;
&lt;li&gt;videos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI transforms these into connected cultural experiences.&lt;/p&gt;

&lt;p&gt;A grandmother's recipe is not just a recipe.&lt;/p&gt;

&lt;p&gt;It is connected to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migration&lt;/li&gt;
&lt;li&gt;family history&lt;/li&gt;
&lt;li&gt;geography&lt;/li&gt;
&lt;li&gt;tradition&lt;/li&gt;
&lt;li&gt;identity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every story becomes part of the global curiosity map.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Passion Atlas is an AI-native application built using specialised agents.&lt;/p&gt;
&lt;h3&gt;
  
  
  Passion Extraction Agent
&lt;/h3&gt;

&lt;p&gt;Transforms human stories into structured curiosity data.&lt;/p&gt;

&lt;p&gt;It identifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;interests&lt;/li&gt;
&lt;li&gt;emotions&lt;/li&gt;
&lt;li&gt;motivations&lt;/li&gt;
&lt;li&gt;cultural context&lt;/li&gt;
&lt;li&gt;related concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;Input:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I love making traditional food with my grandmother."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cooking

Family Heritage

Traditional Knowledge

Cultural Preservation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Curiosity Graph Agent
&lt;/h3&gt;

&lt;p&gt;Builds relationships between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passions&lt;/li&gt;
&lt;li&gt;stories&lt;/li&gt;
&lt;li&gt;locations&lt;/li&gt;
&lt;li&gt;cultures&lt;/li&gt;
&lt;li&gt;experiences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The graph continuously evolves as more people contribute.&lt;/p&gt;
&lt;h3&gt;
  
  
  Discovery Agent
&lt;/h3&gt;

&lt;p&gt;Finds unexpected connections.&lt;/p&gt;

&lt;p&gt;It considers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;novelty&lt;/li&gt;
&lt;li&gt;relevance&lt;/li&gt;
&lt;li&gt;emotional connection&lt;/li&gt;
&lt;li&gt;cultural depth&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Cultural Preservation Agent
&lt;/h3&gt;

&lt;p&gt;Transforms human experiences into meaningful knowledge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;summaries&lt;/li&gt;
&lt;li&gt;translations&lt;/li&gt;
&lt;li&gt;context&lt;/li&gt;
&lt;li&gt;connected discovery paths&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx7gukm0vetes211d0ytg.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx7gukm0vetes211d0ytg.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/YeA5tKSIwds"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The app currently runs on local as it needs environment values. However, a simulation version is deployed on Vercel.&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://passion-atlas.vercel.app/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;passion-atlas.vercel.app&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ujjavala" rel="noopener noreferrer"&gt;
        ujjavala
      &lt;/a&gt; / &lt;a href="https://github.com/ujjavala/passion-atlas" rel="noopener noreferrer"&gt;
        passion-atlas
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🌌 Passion Atlas&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A living map of human curiosity.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Passion Atlas is an AI-powered map that helps people discover how their interests, experiences, cultures, and ideas connect. Instead of asking &lt;em&gt;"What is your passion?"&lt;/em&gt;, it asks &lt;em&gt;"What are all the things that make you curious — and how do they connect?"&lt;/em&gt; and renders the answer as a personal &lt;strong&gt;Passion Genome&lt;/strong&gt;: a constellation of your curiosity where unexpected bridges between seemingly unrelated interests light up.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your interests are not separate islands. They are a connected universe.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/weekend-2026-07-09" rel="nofollow"&gt;dev.to Weekend Challenge: Passion Edition&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Each screen is a different lens on the same living curiosity graph:&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Screen&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🌌 &lt;strong&gt;Atlas&lt;/strong&gt; (Canvas)&lt;/td&gt;
&lt;td&gt;Enter a passion and watch it expand into a constellation. Drill into any node to discover its connections; claim ownership of a discovery path on-chain.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🧬 &lt;strong&gt;Genome&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Your personal "Passion Genome" — the&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ujjavala/passion-atlas" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🏆 Best Use of Google AI
&lt;/h3&gt;

&lt;p&gt;Google Gemini powers the reasoning layer.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7w03mzeisyjfdgw2vgj9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7w03mzeisyjfdgw2vgj9.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It helps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extract hidden passions from stories&lt;/li&gt;
&lt;li&gt;understand semantic relationships&lt;/li&gt;
&lt;li&gt;generate Passion Genomes&lt;/li&gt;
&lt;li&gt;discover unexpected connections&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm9f0sxupm3voc9j2vbnv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm9f0sxupm3voc9j2vbnv.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gemini enables the system to reason across thousands of possible curiosity pathways.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏆 Best Use of Snowflake
&lt;/h3&gt;

&lt;p&gt;Snowflake powers the global Passion Graph.&lt;/p&gt;

&lt;p&gt;It stores and analyses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passion relationships&lt;/li&gt;
&lt;li&gt;cultural stories&lt;/li&gt;
&lt;li&gt;discovery patterns&lt;/li&gt;
&lt;li&gt;human curiosity trends&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ee13jadcliywmkdevli.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ee13jadcliywmkdevli.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over time, the system can uncover patterns like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"People interested in ceramics often explore gardening, architecture, and sustainable design."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The graph becomes richer with every contribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏆 Best Use of ElevenLabs
&lt;/h3&gt;

&lt;p&gt;Passion is emotional.&lt;/p&gt;

&lt;p&gt;A story is not always the same when read as text.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn67x8s9m3gg2zlwzdlgy.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn67x8s9m3gg2zlwzdlgy.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ElevenLabs enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;voice-based memories&lt;/li&gt;
&lt;li&gt;multilingual storytelling&lt;/li&gt;
&lt;li&gt;emotional narration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A craftsperson can share their knowledge in their own voice.&lt;/p&gt;

&lt;p&gt;A family story can remain human.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏆 Best Use of Solana
&lt;/h3&gt;

&lt;p&gt;Passion Atlas introduces a contribution layer.&lt;/p&gt;

&lt;p&gt;Human knowledge creates value, but contributors are often invisible.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ojinfc7776ouoh3j0nx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ojinfc7776ouoh3j0nx.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Solana enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transparent recognition&lt;/li&gt;
&lt;li&gt;contributor rewards&lt;/li&gt;
&lt;li&gt;community incentives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traditional Weaving Story

↓

500 people discovered this tradition

↓

Contributor recognised

↓

Community preservation supported
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is not to financialise every passion.&lt;/p&gt;

&lt;p&gt;The goal is to recognise people who preserve and share human knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Passion Atlas?
&lt;/h2&gt;

&lt;p&gt;The internet already connects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;people&lt;/li&gt;
&lt;li&gt;places&lt;/li&gt;
&lt;li&gt;products&lt;/li&gt;
&lt;li&gt;information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it does not connect something deeply human:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;curiosity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe having many passions is not a lack of direction.&lt;/p&gt;

&lt;p&gt;Maybe it is a different way of exploring the world.&lt;/p&gt;

&lt;p&gt;Our interests are not random dots.&lt;/p&gt;

&lt;p&gt;They are a map.&lt;/p&gt;

&lt;p&gt;Passion Atlas helps us discover the connections.&lt;/p&gt;

&lt;p&gt;Because every passion leads somewhere.&lt;/p&gt;

&lt;p&gt;And every person carries a universe of curiosity waiting to be explored.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>gemini</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built a Turing inspired Treasure-Run Game Where Australia Saves the Sun</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Wed, 10 Jun 2026 08:41:06 +0000</pubDate>
      <link>https://dev.to/ujja/how-i-built-a-treasure-run-game-where-australia-saves-the-sun-kck</link>
      <guid>https://dev.to/ujja/how-i-built-a-treasure-run-game-where-australia-saves-the-sun-kck</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/june-game-jam-2026-06-03"&gt;June Solstice Game Jam&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;For this game jam, I built &lt;strong&gt;Dawn Dashers&lt;/strong&gt;, an adventure runner set during the &lt;strong&gt;June Solstice&lt;/strong&gt;, the longest night of the year in Australia.&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%2F16y6v7b129gqa9ah1wfh.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%2F16y6v7b129gqa9ah1wfh.png" alt="Dawn Dashers" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea started with a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if the longest night didn't end?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Dawn Dashers, a rogue machine called the &lt;strong&gt;Turing Engine&lt;/strong&gt; has stolen the sunrise and scattered seven &lt;strong&gt;Sun Fragments&lt;/strong&gt; across Australia. Players race through deserts, bushland trails, coastal cliffs, and hidden ruins to recover the fragments and bring daylight back.&lt;/p&gt;

&lt;p&gt;The game takes inspiration from the June solstice, but with an Australian treasure-hunting twist. Along the way, players dodge obstacles, collect relics, unlock animal abilities, and solve quick logic puzzles inspired by Alan Turing.&lt;/p&gt;

&lt;p&gt;One thing I wanted to avoid was making the solstice just part of the story. Instead, it's part of the gameplay itself. At the start, the world is trapped in darkness. As players recover Sun Fragments, the environment gradually becomes brighter, warmer, and more alive until the final sunrise returns.&lt;/p&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build something fun first, while using the June Solstice as the heart of the adventure.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Video Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/x-U4mGISDJw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can also try: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running through the Australian Outback&lt;/li&gt;
&lt;li&gt;Discovering treasure clues&lt;/li&gt;
&lt;li&gt;Solving Turing-inspired logic challenges&lt;/li&gt;
&lt;li&gt;Collecting Sun Fragments&lt;/li&gt;
&lt;li&gt;Unlocking animal abilities&lt;/li&gt;
&lt;li&gt;Watching the world transform from the longest night back into daylight&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... directly through the deployed game (using Vercel)  &lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://dawn-dashers.vercel.app/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;dawn-dashers.vercel.app&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ujjavala/dawn-dashers" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vanilla JavaScript — game.js is the entire game engine (~6500+ lines)&lt;/li&gt;
&lt;li&gt;Three.js — 3D terrain rendering behind the 2D gameplay HUD&lt;/li&gt;
&lt;li&gt;Web Audio API&lt;/li&gt;
&lt;li&gt;HTML/CSS — index.html, inline UI modals, HUD&lt;/li&gt;
&lt;li&gt;GitHub Copilot&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I started by focusing on the core gameplay loop:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run → Dodge → Collect → Discover → Restore Light&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once movement felt fun, I started layering in everything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making the Solstice Matter
&lt;/h3&gt;

&lt;p&gt;The biggest design decision was making the June Solstice an actual game mechanic rather than just background lore.&lt;/p&gt;

&lt;p&gt;The game begins during the longest night of the year. Every Sun Fragment recovered pushes the world closer to dawn.&lt;/p&gt;

&lt;p&gt;As players progress:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Darkness slowly recedes&lt;/li&gt;
&lt;li&gt;New areas become visible&lt;/li&gt;
&lt;li&gt;Lighting becomes warmer&lt;/li&gt;
&lt;li&gt;The world feels more hopeful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of the game, the player has literally restored the sunrise.&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%2Fz8whca9yjdou397ijbrs.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%2Fz8whca9yjdou397ijbrs.png" alt="sunrise" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building an Australian Adventure
&lt;/h3&gt;

&lt;p&gt;Rather than using generic fantasy locations, I wanted the game to feel distinctly Australian.&lt;/p&gt;

&lt;p&gt;The adventure takes players through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outback ruins&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%2Fc5eb17t5589234ik89b6.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%2Fc5eb17t5589234ik89b6.png" alt="Outback ruins" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bushland trails&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%2Fef58l291wdj82rhb9he7.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%2Fef58l291wdj82rhb9he7.png" alt="Bushland trails" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A quirky roadside servo&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%2Fukoa6ijxa1dqa06nu2r5.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%2Fukoa6ijxa1dqa06nu2r5.png" alt="A quirky roadside servo" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coastal lighthouse cliffs&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%2Fetnel2w3xcvxyyprkpzs.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%2Fetnel2w3xcvxyyprkpzs.png" alt="Coastal lighthouse cliffs" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mangroves&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%2Fg0ztu58sdhu4qhjp85jo.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%2Fg0ztu58sdhu4qhjp85jo.png" alt="Mangroves" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blue Mountains&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%2Fmlc2jsga9j9bg74gz8md.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%2Fmlc2jsga9j9bg74gz8md.png" alt="Blue Mountains" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nullarbor&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%2Fuw812u5j9mrvkods4374.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%2Fuw812u5j9mrvkods4374.png" alt="Nullarbor" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Observatory&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%2Fc2nbxpisx4c9180rbmn0.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%2Fc2nbxpisx4c9180rbmn0.png" alt="Observatory" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aurora-lit Tasmania&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%2F3np9bqwy5v1e8sgucpyr.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%2F3np9bqwy5v1e8sgucpyr.png" alt="Aurora-lit Tasmania" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each region introduces new obstacles and visual themes while keeping the action moving.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Nod to Alan Turing
&lt;/h3&gt;

&lt;p&gt;Since June is also Alan Turing's birth month, I wanted to include a tribute without turning the game into a history lesson.&lt;/p&gt;

&lt;p&gt;The game's antagonist, the &lt;strong&gt;Turing Engine&lt;/strong&gt;, believes that endless night is the most logical state of existence.&lt;/p&gt;

&lt;p&gt;Players encounter quick puzzles based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pattern recognition&lt;/li&gt;
&lt;li&gt;Binary switches&lt;/li&gt;
&lt;li&gt;Signal routing&lt;/li&gt;
&lt;li&gt;Light circuits&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%2F4y3cf5wd54puvigy02px.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%2F4y3cf5wd54puvigy02px.png" alt="Puzzle" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The puzzles are short and designed to support the action rather than interrupt it.&lt;/p&gt;

&lt;h3&gt;
  
  
  TM Corridors — &lt;em&gt;Turing's Run&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;At three points in the expedition, the world stops being a runner and becomes a &lt;strong&gt;Turing Machine you operate by hand&lt;/strong&gt;. The level fades away and you step into a glowing sci-fi &lt;strong&gt;corridor&lt;/strong&gt; rendered in one-point perspective — a hallway that &lt;em&gt;is&lt;/em&gt; the tape, charging toward a vanishing-point portal.&lt;/p&gt;

&lt;p&gt;The story is told in three chapters, framed as &lt;strong&gt;Alan Turing bypassing three sealed gates&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46dfjh99xmhttnxup334.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46dfjh99xmhttnxup334.png" alt="Bit Flipper" width="799" height="403"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0rw901sddt7q34blp28.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0rw901sddt7q34blp28.png" alt="Parity Checker" width="799" height="403"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgi6uy40nzutu04mklodb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgi6uy40nzutu04mklodb.png" alt="Even-Length Protocol" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Corridor&lt;/th&gt;
&lt;th&gt;Machine&lt;/th&gt;
&lt;th&gt;The gate demands&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;I of III&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bit Flipper&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Invert every bit until the blank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;II of III&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Parity Checker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open only for an even number of &lt;code&gt;1&lt;/code&gt;s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;III of III&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Even-Length Protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Accept only an even-length message&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Art Direction
&lt;/h3&gt;

&lt;p&gt;One of the biggest pivots during development was moving away from a bright arcade aesthetic and leaning into a treasure-hunting adventure style.&lt;/p&gt;

&lt;p&gt;The world uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Warm desert colours&lt;/li&gt;
&lt;li&gt;Ancient ruins&lt;/li&gt;
&lt;li&gt;Dust particles&lt;/li&gt;
&lt;li&gt;Golden sunlight effects&lt;/li&gt;
&lt;li&gt;Treasure-map inspired UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This direction helped tie together the themes of the solstice, exploration, and treasure hunt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technology
&lt;/h3&gt;

&lt;p&gt;The game was built using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vanilla JavaScript — game.js is the entire game engine (~6500+ lines)&lt;/li&gt;
&lt;li&gt;Three.js — 3D terrain rendering behind the 2D gameplay HUD&lt;/li&gt;
&lt;li&gt;Web Audio API&lt;/li&gt;
&lt;li&gt;HTML/CSS — index.html, inline UI modals, HUD&lt;/li&gt;
&lt;li&gt;Vercel Deployment&lt;/li&gt;
&lt;li&gt;GitHub Copilot for development acceleration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The focus throughout was on keeping the project lightweight, responsive, and playable directly in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Best Ode to Alan Turing
&lt;/h3&gt;

&lt;p&gt;Dawn Dashers is a love letter to Alan Turing, the mathematician, codebreaker, and father of Theoretical computer scientist who was born on 23 June 1912.&lt;/p&gt;

&lt;p&gt;Rather than focusing on historical events, I wanted to celebrate the ideas that made his work so influential: logic, patterns, problem-solving, and computation.&lt;/p&gt;

&lt;p&gt;The Turing Engine serves as the main antagonist, and players overcome its challenges through quick puzzles inspired by computational thinking.&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%2Ffvxsvf7aop53f5rhsjg7.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%2Ffvxsvf7aop53f5rhsjg7.png" alt="Treasure game" width="799" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;P.S. For fellow nerds (like me 😄), each puzzle comes with a little extra bit of knowledge. Just click "Learn why this puzzle matters".&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  In the characters
&lt;/h4&gt;

&lt;p&gt;The 27 playable dashers are all Australian animals  and each character's &lt;strong&gt;power&lt;/strong&gt; and &lt;strong&gt;quirk&lt;/strong&gt; are inspired by&lt;br&gt;
concepts from Turing's work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Emu&lt;/em&gt; — relentless forward motion (Turing's unstoppable curiosity)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Wombat&lt;/em&gt; — burrow-dodge (evasion, steganography)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Echidna&lt;/em&gt; — spiny defence (cryptographic hardening)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Cassowary&lt;/em&gt; — apex predator speed (Colossus, the world's first electronic computer)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "special" dashers can only be unlocked by solving enough treasure puzzles — a direct nod to&lt;br&gt;
the idea that Turing's deeper insights were hidden behind layers of prior work.&lt;/p&gt;
&lt;h4&gt;
  
  
  In the Game engine
&lt;/h4&gt;

&lt;p&gt;The game's lifecycle is modelled as a &lt;strong&gt;real Turing Machine&lt;/strong&gt; (see &lt;em&gt;Architecture&lt;/em&gt; below).&lt;br&gt;
States are labelled &lt;code&gt;q_*&lt;/code&gt;, input symbols &lt;code&gt;σ_*&lt;/code&gt;, and every transition is written to a &lt;strong&gt;tape&lt;/strong&gt;&lt;br&gt;
with a head that advances &lt;code&gt;R&lt;/code&gt;, stays &lt;code&gt;S&lt;/code&gt;, or — on a level abort — rewinds &lt;code&gt;L&lt;/code&gt;.&lt;br&gt;
This isn't just a metaphor: the implementation is a literal δ(q, σ) → (q′, dir) transition table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────┐
│  Browser                                                            │
│                                                                     │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │  game.js  (core)                                             │   │
│  │                                                              │   │
│  │   Run loop / input                                           │   │
│  │        │                                                     │   │
│  │        │  sendFlow(σ_*)        ┌─────────────────────────┐   │   │
│  │        ├─────────────────────► │   game-state.js         │   │   │
│  │        │  ◄── state flags ──── │                         │   │   │
│  │        │                       │   Turing Machine        │   │   │
│  │        │                       │   Q  states  (q_*)      │   │   │
│  │        │                       │   Σ  symbols (σ_*)      │   │   │
│  │        │                       │   δ  transition table   │   │   │
│  │        │                       │   tape[]  head ptr      │   │   │
│  │        │                       └─────────────────────────┘   │   │
│  │        │                                                      │   │
│  │        ├── Puzzle pick &amp;amp; dedup ──► puzzle-tracker.js          │   │
│  │        │                               │                      │   │
│  │        │                          puzzle-filter.js            │   │
│  │        │                               │                      │   │
│  │        │                          puzzle-data.js              │   │
│  │        │                          ├── questions-easy.js       │   │
│  │        │                          ├── questions-medium.js     │   │
│  │        │                          └── questions-hard.js       │   │
│  │        │                                                      │   │
│  │        ├── Three.js + Canvas render                           │   │
│  │        │        └── terrain-styles.js                         │   │
│  │        │                                                      │   │
│  │        └── Config reads                                       │   │
│  │             ├── character-config.js                           │   │
│  │             ├── game-data.js                                   │   │
│  │             └── game-settings.js                              │   │
│  │                                                               │   │
│  │  game-ui.js ──ctx callbacks──► game.js                        │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  index.html  (loads all modules via &amp;lt;script&amp;gt; tags)                  │
└─────────────────────────────────────────────────────────────────────┘
         ▲
         │  served by
┌────────┴────────┐
│  Nginx (Docker) │
│  docker-compose │
└─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Best Google AI Usage
&lt;/h3&gt;

&lt;p&gt;I'm also submitting for &lt;strong&gt;Best Google AI Usage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'll be honest: I'm much more comfortable building systems and writing backend code than designing beautiful interfaces.&lt;/p&gt;

&lt;p&gt;One of the challenges during this project was figuring out how to make &lt;em&gt;Dawn Dashers&lt;/em&gt; feel cohesive visually. I had a rough idea of the atmosphere I wanted—an Australian treasure-hunting adventure, but translating that into colours, layouts, typography, and UI design isn't my strongest skill.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Google Stitch&lt;/strong&gt; helped.&lt;/p&gt;

&lt;p&gt;I used Stitch to explore rapidly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI layouts and screen structure&lt;/li&gt;
&lt;li&gt;Menu and HUD designs&lt;/li&gt;
&lt;li&gt;Colour palettes&lt;/li&gt;
&lt;li&gt;Typography combinations&lt;/li&gt;
&lt;li&gt;Visual hierarchy&lt;/li&gt;
&lt;li&gt;Overall art direction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What impressed me most was how easy it was to iterate. Instead of spending hours moving elements around in a design tool, I could describe the feeling I wanted and quickly generate multiple directions to evaluate.&lt;/p&gt;

&lt;p&gt;For example, I started with a fairly generic arcade-style look, but through a few iterations in Stitch, I landed on a much stronger visual identity built around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Warm desert colours&lt;/li&gt;
&lt;li&gt;Treasure-map inspired UI&lt;/li&gt;
&lt;li&gt;Gold accents&lt;/li&gt;
&lt;li&gt;Weathered parchment styling&lt;/li&gt;
&lt;li&gt;Adventure-game aesthetics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those explorations directly influenced the final design language of the game.&lt;/p&gt;

&lt;p&gt;I also used Gemini extensively during the design phase.&lt;/p&gt;

&lt;p&gt;One challenge was incorporating Alan Turing-inspired puzzles without making the game feel like a classroom exercise. I wanted puzzles based on logic, patterns, and computation, but they also needed to feel like they belonged in an Australian treasure-hunting adventure.&lt;/p&gt;

&lt;p&gt;Gemini helped me brainstorm and refine puzzle concepts such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pattern recognition challenges&lt;/li&gt;
&lt;li&gt;Binary switch puzzles&lt;/li&gt;
&lt;li&gt;Signal routing mechanics&lt;/li&gt;
&lt;li&gt;Treasure clues tied to Australian locations and wildlife&lt;/li&gt;
&lt;li&gt;Turing-inspired logic challenges that could be solved quickly during gameplay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, I used Gemini to take abstract puzzle ideas and re-theme them into Australian contexts, turning generic logic puzzles into clues involving lighthouses, Outback landmarks, wildlife, and hidden Sun Fragments.&lt;/p&gt;

&lt;p&gt;The combination of Stitch and Gemini helped bridge two areas where I typically spend a lot of time: design exploration and content creation. Instead of starting from a blank page, I could rapidly iterate on ideas, evaluate options, and focus more of my time on building and polishing the game itself.&lt;/p&gt;

&lt;p&gt;The AI tools didn't replace the creative decisions—they helped me reach better ones faster.&lt;/p&gt;

&lt;p&gt;As a solo developer, having a tool that could help bridge the gap between "I know what I want it to feel like" and "I know how to design it" was incredibly valuable. It let me spend more time building gameplay while still ending up with a much more polished visual experience.&lt;/p&gt;

&lt;p&gt;Google Stitch didn't replace the design decisions—it helped me discover and refine them much faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Happy Dashing !!!
&lt;/h3&gt;

&lt;p&gt;Thanks for checking out Dawn Dashers 🌅&lt;/p&gt;

&lt;p&gt;It was a lot of fun combining the June Solstice, Australian landscapes, treasure hunting, arcade runners, and a small nod to Alan Turing into one adventure.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gamechallenge</category>
      <category>gamedev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Is Zero Trust Enough for Agentic Systems?</title>
      <dc:creator>ujja</dc:creator>
      <pubDate>Tue, 02 Jun 2026 13:45:15 +0000</pubDate>
      <link>https://dev.to/ujja/is-zero-trust-enough-for-agentic-systems-ge9</link>
      <guid>https://dev.to/ujja/is-zero-trust-enough-for-agentic-systems-ge9</guid>
      <description>&lt;p&gt;I’ve always had a soft spot for authentication systems.&lt;/p&gt;

&lt;p&gt;About seven years ago, I started working on auth, and something just &lt;em&gt;clicked&lt;/em&gt;. What began as login flows slowly turned into a deeper curiosity about identity, permissions, and how systems decide who gets to do what.&lt;/p&gt;

&lt;p&gt;Over time, I’ve worked with tools like Keycloak, Auth0, Okta, and Ping Identity. Different platforms, same core idea kept showing up:&lt;/p&gt;

&lt;p&gt;Never trust. Always verify.&lt;/p&gt;

&lt;p&gt;For a long time, that felt like the finish line.&lt;/p&gt;

&lt;p&gt;Lately, though, it’s starting to feel more like the starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The moment systems stop responding… and start acting&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;During a recent hackathon, I built something called PlanetLedger. The idea was simple: upload your bank statement to see your environmental impact.&lt;/p&gt;

&lt;p&gt;But under the hood, it didn’t behave like a typical app.&lt;/p&gt;

&lt;p&gt;An upload didn’t just return a response. It kicked off a series of actions. First, it parsed transactions. Then, it categorised vendors. Next, it calculated a score. It also generated insights and triggered notifications. Finally, it updated a memory timeline for future runs.&lt;/p&gt;

&lt;p&gt;All of that started from a single call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;openClawChainedTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;previousScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Which quietly unfolded into:&lt;/p&gt;

&lt;p&gt;transactions_uploaded → score_calculated → insights_generated → score_improved&lt;/p&gt;

&lt;p&gt;At some point while building this, I realised something had changed.&lt;/p&gt;

&lt;p&gt;This wasn’t a request-response system anymore.&lt;/p&gt;

&lt;p&gt;It was a system that &lt;em&gt;kept acting&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Where Zero Trust fits perfectly… and where it doesn’t&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;From a security perspective, I did everything by the book.&lt;/p&gt;

&lt;p&gt;Every API route checks whether the agent is allowed to perform an action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;canPerform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&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;required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FGA_RULES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;]?.[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;Scopes are tightly defined. Every action is verified. Nothing runs without permission.&lt;/p&gt;

&lt;p&gt;If you look at this through a Zero Trust lens, it’s solid.&lt;/p&gt;

&lt;p&gt;And yet, the most interesting problems I ran into had nothing to do with access.&lt;/p&gt;

&lt;p&gt;They showed up &lt;em&gt;after&lt;/em&gt; access was granted.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The real question isn’t “can it act?” — it’s “should it keep acting?”&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Take something as simple as a high-impact alert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;highImpactAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pseudonymize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;score&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;impactScore&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;pushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;high_impact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;High-Impact Alert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highImpactCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;impact&lt;/span&gt; &lt;span class="nx"&gt;transactions&lt;/span&gt; &lt;span class="nx"&gt;detected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&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;Everything here is valid.&lt;/p&gt;

&lt;p&gt;The user is authenticated. The system has permission. The action is allowed.&lt;/p&gt;

&lt;p&gt;But there’s a more subtle question hiding underneath:&lt;/p&gt;

&lt;p&gt;Should this alert be triggered right now?&lt;/p&gt;

&lt;p&gt;Because that depends on things Zero Trust doesn’t see.&lt;/p&gt;

&lt;p&gt;Maybe the score calculation was slightly off. The categorisation step earlier in the pipeline may have misfired. Maybe the context wasn’t complete yet.&lt;/p&gt;

&lt;p&gt;Each step is correct individually.&lt;/p&gt;

&lt;p&gt;But the outcome might still be… wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;When valid steps create questionable behaviour&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One of the most interesting things about the OpenClaw pipeline was how easy it was to compose behaviour.&lt;/p&gt;

&lt;p&gt;You can attach multiple workflows to the same event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;registerOpenClawTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transactions_uploaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autoInsightOnUpload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nf"&gt;registerOpenClawTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transactions_uploaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highImpactAlert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On their own, these are harmless.&lt;/p&gt;

&lt;p&gt;One generates insights. The other sends alerts.&lt;/p&gt;

&lt;p&gt;But together, they begin to shape how a user interprets their financial behaviour. Add weekly reports, detect patterns, and make recommendations. Then, the system starts to influence decisions.&lt;/p&gt;

&lt;p&gt;And that’s where things get tricky.&lt;/p&gt;

&lt;p&gt;Because even if every individual step is valid, the &lt;em&gt;overall direction&lt;/em&gt; can drift.&lt;/p&gt;

&lt;p&gt;Zero Trust doesn’t track that. It validates moments, not trajectories.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Intent is where things start to slip&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;PlanetLedger uses a RAG layer to generate insights grounded in user data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ragContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildRagContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&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;insights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildAgentInsights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;userContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ragContext&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works surprisingly well most of the time.&lt;/p&gt;

&lt;p&gt;But occasionally, you’ll see something slightly off. A recommendation that technically makes sense but doesn’t quite match what you’d expect. A pattern that’s overemphasised. A suggestion that feels a bit… disconnected.&lt;/p&gt;

&lt;p&gt;Nothing is broken.&lt;/p&gt;

&lt;p&gt;But something feels misaligned.&lt;/p&gt;

&lt;p&gt;That’s the gap.&lt;/p&gt;

&lt;p&gt;Zero Trust ensures the system is allowed to act. It doesn’t ensure the system is acting &lt;em&gt;with the right intent&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The part that surprised me: drift&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The more I worked with chained workflows, the more I noticed this subtle effect.&lt;/p&gt;

&lt;p&gt;If something is slightly off early in the pipeline — say, a categorisation edge case — that error doesn’t stay isolated. It propagates.&lt;/p&gt;

&lt;p&gt;It affects scoring. Which affects insights. Which affects alerts. Which affects what the user sees.&lt;/p&gt;

&lt;p&gt;By the time it surfaces, it’s no longer obvious where it started.&lt;/p&gt;

&lt;p&gt;Everything along the way was technically valid.&lt;/p&gt;

&lt;p&gt;But the outcome feels wrong.&lt;/p&gt;

&lt;p&gt;That’s drift.&lt;/p&gt;

&lt;p&gt;And it’s not something traditional access control is designed to catch.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;So what actually helps?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I didn’t sit down with a framework for this. Most of these ideas came from trying to make PlanetLedger behave more predictably.&lt;/p&gt;

&lt;p&gt;One thing that helped was thinking beyond request-level authorisation.&lt;/p&gt;

&lt;p&gt;Because the pipeline continues after the initial API response, decisions are being made in a flow, not a single moment. That means authorisation needs to become aware of state, timing, and sequence — not just whether a token is valid.&lt;/p&gt;

&lt;p&gt;Another thing that made a difference was leaning into deterministic rules where it mattered. The scoring system, for example, is intentionally simple and explainable. Not because an LLM couldn’t do it, but because predictability is a form of control.&lt;/p&gt;

&lt;p&gt;The structure of OpenClaw itself also acts as a constraint. It’s deliberately minimal — no retries, no replay, no distributed guarantees. At first, that feels like a limitation, but it actually forces the system to operate within clear, bounded behaviour. It can only do what the registered workflows define.&lt;/p&gt;

&lt;p&gt;Logging was another area that evolved quickly. Moving from plain logs to structured outputs made it easier to trace what happened. But even that surfaced a deeper need: understanding &lt;em&gt;why&lt;/em&gt; a decision was made, not just what happened.&lt;/p&gt;

&lt;p&gt;And then there’s step-up.&lt;/p&gt;

&lt;p&gt;In traditional systems, step-up authentication is about verifying identity at critical moments. But in systems like this, identity isn’t usually the weak point. The question isn’t “is this really the user?”&lt;/p&gt;

&lt;p&gt;It’s “Should this decision go through?”&lt;/p&gt;

&lt;p&gt;That shift—from checking identities to validating decisions—looks small. But it changes how you make safeguards.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What this starts to look like in practice&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After building something like PlanetLedger, the architecture stops being just about access control.&lt;/p&gt;

&lt;p&gt;Zero Trust still sits at the base, making sure only the right actors can do the right things.&lt;/p&gt;

&lt;p&gt;But on top of that, you start layering systems that understand behaviour over time. Systems guide actions. They spot patterns and notice when things feel off. Sometimes, they bring a human back in when the stakes are high.&lt;/p&gt;

&lt;p&gt;None of these replaces Zero Trust.&lt;/p&gt;

&lt;p&gt;They fill in the gaps that it was never designed to cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Final thought&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Zero Trust is still essential.&lt;/p&gt;

&lt;p&gt;Without it, systems like PlanetLedger wouldn’t hold up for a second.&lt;/p&gt;

&lt;p&gt;But once systems move from simply responding to requests to continuously making decisions…&lt;/p&gt;

&lt;p&gt;Trust stops being something you verify once.&lt;/p&gt;

&lt;p&gt;It becomes something you evaluate, quietly and continuously, across everything the system does.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>security</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
