<?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: taiwo</title>
    <description>The latest articles on DEV Community by taiwo (@taiwofamaks).</description>
    <link>https://dev.to/taiwofamaks</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F697617%2F5d030e9e-9bcb-4a99-9be3-fb4a91e31a61.jpeg</url>
      <title>DEV Community: taiwo</title>
      <link>https://dev.to/taiwofamaks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/taiwofamaks"/>
    <language>en</language>
    <item>
      <title>Teaching Machines the Art of Nuance with Google Cloud Natural Language API</title>
      <dc:creator>taiwo</dc:creator>
      <pubDate>Wed, 31 Dec 2025 22:47:42 +0000</pubDate>
      <link>https://dev.to/taiwofamaks/teaching-machines-the-art-of-nuance-with-google-cloud-natural-language-api-5cmn</link>
      <guid>https://dev.to/taiwofamaks/teaching-machines-the-art-of-nuance-with-google-cloud-natural-language-api-5cmn</guid>
      <description>&lt;p&gt;Keywords are a blunt instrument for a sharp problem. Everyday, all kinds of users leave a digital trail of reviews, tweets, and support tickets on the internet. For a long time, the best we could do was keyword matching, searching for words like "bad" or "excellent." But human language is far more nuanced than a simple checklist of adjectives.&lt;/p&gt;

&lt;p&gt;I’ve been exploring &lt;strong&gt;Entity and Sentiment Analysis with Natural Language API on Google Cloud&lt;/strong&gt;, and I think it's pretty cool for developers who want to move past simple text search and into true context-awareness. Whether you’re building an intelligent support desk or a global trend-tracking engine, this API moves the needle from simple data processing to genuine human understanding. Here's why:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Power of Entity.
&lt;/h3&gt;

&lt;p&gt;Traditional search looks for strings. The Natural Language API looks for Entities. Take a look at the request and response below:&lt;/p&gt;

&lt;p&gt;Request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "document":{
    "type":"PLAIN_TEXT",
    "content":"Masashi Kishimoto is a Japanese manga artist who is best known for creating the Naruto series, which became one of the best-selling manga in history."
  },
  "encodingType":"UTF8"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "entities": [
    {
      "name": "Masashi Kishimoto",
      "type": "PERSON",
      "metadata": {
        "mid": "/m/01v8_m",
        "wikipedia_url": "https://en.wikipedia.org/wiki/Masashi_Kishimoto"
      },
      "salience": 0.82,
      "mentions": [
        {
          "text": {
            "content": "Masashi Kishimoto",
            "beginOffset": 0
          },
          "type": "PROPER"
        }
      ]
    },
    {
      "name": "Naruto",
      "type": "WORK_OF_ART",
      "metadata": {
        "mid": "/m/01_f69",
        "wikipedia_url": "https://en.wikipedia.org/wiki/Naruto"
      },
      "salience": 0.12,
      "mentions": [
        {
          "text": {
            "content": "Naruto",
            "beginOffset": 64
          },
          "type": "PROPER"
        }
      ]
    },
    {
      "name": "manga artist",
      "type": "COMMON",
      "salience": 0.05,
      "mentions": [
        {
          "text": {
            "content": "manga artist",
            "beginOffset": 32
          },
          "type": "COMMON"
        }
      ]
    }
  ],
  "language": "en"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I fed the API a sentence about Masashi Kishimoto, and it didn't just see a name. It identified him as a &lt;code&gt;PERSON&lt;/code&gt;, linked his Wikipedia page, correctly categorized Naruto as a &lt;code&gt;WORK_OF_ART&lt;/code&gt; and linked the Wikipedia page as well, and most importantly, calculated his &lt;strong&gt;Salience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What's Salience? It's a &lt;strong&gt;0.0&lt;/strong&gt; to &lt;strong&gt;1.0&lt;/strong&gt; score that tells you how central an entity is to the text. This allows us to build apps that can automatically summarize what a document is actually about, rather than just listing every name mentioned.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;metadata&lt;/code&gt; field is also pretty impressive. The API provides a &lt;code&gt;mid&lt;/code&gt; (Machine ID) that links to the Google Knowledge Graph. This means your application instantly gains a massive context knowing that 'Naruto' isn't just a word, but a global franchise with a specific history, all without you having to build a database yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Sentiment vs. Magnitude: The Volume of Emotion.
&lt;/h3&gt;

&lt;p&gt;Most sentiment analysis gives you a "positive" or "negative" score. Google Cloud adds another dimension: Magnitude.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Score: Is it positive or negative? (-1.0 to 1.0)&lt;/li&gt;
&lt;li&gt;Magnitude: How much emotion is being expressed? (0 to ∞)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine a customer review that says: "&lt;em&gt;The food was amazing, but the waiter was incredibly rude!&lt;/em&gt;" A simple sentiment tool might average this to "Neutral" (0.0). But the Natural Language API will show a high magnitude. This tells the developer: "&lt;em&gt;This user isn't indifferent; they are feeling strong, conflicting emotions.&lt;/em&gt;" That is a signal you can't get from a basic score.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Game Changer: Entity Sentiment Analysis.
&lt;/h3&gt;

&lt;p&gt;This is where the tech gets truly useful. In the past, if a customer wrote a long paragraph praising your meals but hating your restaurant service, the vibe of the review would be mixed.&lt;/p&gt;

&lt;p&gt;With Entity Sentiment, the API breaks it down. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product: Positive (+0.9)&lt;/li&gt;
&lt;li&gt;Service: Negative (-0.8)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a developer, you can now build a dashboard that tells the business owner exactly what to fix. So they aren't just seeing "&lt;em&gt;customers are unhappy&lt;/em&gt;"; they’re also seeing "&lt;em&gt;customers love the jollof rice, but the service needs help.&lt;/em&gt;"&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Multilingual Support.
&lt;/h3&gt;

&lt;p&gt;One of the most impressive magic tricks of this API is its multilingual capability. I tested it with Japanese text: &lt;br&gt;
日本のグーグルのオフィスは、東京の六本木ヒルズにあります and got the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "entities": [
    {
      "name": "日本",
      "type": "LOCATION",
      "metadata": {
        "mid": "/m/03_3d",
        "wikipedia_url": "https://en.wikipedia.org/wiki/Japan"
      },
      "salience": 0.23854347,
      "mentions": [
        {
          "text": {
            "content": "日本",
            "beginOffset": 0
          },
          "type": "PROPER"
        }
      ]
    },
    {
      "name": "グーグル",
      "type": "ORGANIZATION",
      "metadata": {
        "mid": "/m/045c7b",
        "wikipedia_url": "https://en.wikipedia.org/wiki/Google"
      },
      "salience": 0.21155767,
      "mentions": [
        {
          "text": {
            "content": "グーグル",
            "beginOffset": 9
          },
          "type": "PROPER"
        }
      ]
    },
    ...
  ]
  "language": "ja"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without me telling the API the language, it automatically detected it, identified "Tokyo" as a &lt;code&gt;LOCATION&lt;/code&gt;, and even provided the English Wikipedia link! For a developer building global applications, this level of out-of-the-box intelligence is a massive time-saver.&lt;/p&gt;

&lt;p&gt;Ultimately, it’s not just about the JSON response, but also the insight that response provides. We’ve moved past the era of simple search and keyword matching. By leveraging features like Salience and Entity Sentiment, we can build systems that actually listen to what users are saying, rather than just scanning what they’ve typed.&lt;/p&gt;

&lt;p&gt;The Natural Language API bridges the gap between complex machine learning and real world applications, democratizing high-level AI, making it accessible to any developer with a Cloud Console and a creative idea. It's been a thrill to see how a few lines of code can unlock global context and human nuance (without needing a PhD in linguistics😀). I’m excited to see how we, as a community, use these tools to build more intuitive and inclusive applications🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  DYOR
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.cloud.google.com/natural-language/docs" rel="noopener noreferrer"&gt;Natural Language API Basics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>discuss</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Build an Offline-First Web App with Service Workers</title>
      <dc:creator>taiwo</dc:creator>
      <pubDate>Sun, 12 Oct 2025 19:08:41 +0000</pubDate>
      <link>https://dev.to/taiwofamaks/build-an-offline-first-web-app-with-service-workers-2ml7</link>
      <guid>https://dev.to/taiwofamaks/build-an-offline-first-web-app-with-service-workers-2ml7</guid>
      <description>&lt;p&gt;Browsing your web app is cool and seamless, until the network connection drops. Suddenly, your users are staring at that sad, gray "You are offline" dinosaur, and just like that, the experience is over. It doesn't have to be that way. Your app should load instantly, feel as reliable as a native app, and work even when the network fails.&lt;/p&gt;

&lt;p&gt;Yep. Your web app can work without the internet. That's what Progressive Web Apps (PWAs) and an "offline-first" development gives you.&lt;/p&gt;

&lt;p&gt;In this article, you'll learn exactly how to turn any standard web application into a robust, installable PWA. We'll dive into service workers to cache your app's core files, making your UI invincible to network issues. The principles here are universal, so you can apply them to any frontend project you're working on.&lt;/p&gt;

&lt;p&gt;Ready? Let's go🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do You Need?
&lt;/h2&gt;

&lt;p&gt;A single-page web application. It can be as simple as an &lt;code&gt;index.html&lt;/code&gt; file with some CSS and JavaScript. The key is that it has a few distinct files we can cache, so the main interface (the "app shell") can load even when offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Creating The Web App Manifest
&lt;/h2&gt;

&lt;p&gt;We want to tell the browser about our app and how it should behave when "installed." We do this with a &lt;code&gt;manifest.json&lt;/code&gt; file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the Manifest: In the root of your project, create a new file named &lt;code&gt;manifest.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the Configuration: Paste the following JSON into the file. Be sure to customize the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;short_name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; and &lt;code&gt;icons&lt;/code&gt; for your own project.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "My Awesome App",
  "short_name": "AwesomeApp",
  "start_url": ".",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#007bff",
  "description": "A description of my awesome PWA.",
  "icons": [
    {
      "src": "images/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "images/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember you'll need to save your own icon files in an &lt;code&gt;images&lt;/code&gt; folder for this to work perfectly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Link the Manifest: Open your &lt;code&gt;index.html&lt;/code&gt; and add this line inside the  tag:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="manifest" href="manifest.json"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, browsers like Chrome will now show an "Install" icon in the address bar, allowing users to add your app to their home screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Registering a Service Worker
&lt;/h2&gt;

&lt;p&gt;A Service Worker is a special type of JavaScript file that your browser runs in the background, separate from your web page. It acts like a proxy, allowing network requests to be intercepted  and handled in code. This is the key to offline capability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your project root, create a &lt;code&gt;sw.js&lt;/code&gt; file. Leave it empty for now.&lt;/li&gt;
&lt;li&gt;Register the service worker by telling your main application to install it. Add the following script tag to the bottom of your &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; in &lt;code&gt;index.html&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  if ('serviceWorker' in navigator) {
    window.addEventListener('load', () =&amp;gt; {
      navigator.serviceWorker.register('/sw.js')
        .then(registration =&amp;gt; {
          console.log('service worker registered successfully:', registration);
        })
        .catch(error =&amp;gt; {
          console.log('service worker registration failed:', error);
        });
    });
  }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code checks if the browser supports service workers and, if so, registers the &lt;code&gt;sw.js&lt;/code&gt; file when the page loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Caching the App Shell for Offline Use
&lt;/h2&gt;

&lt;p&gt;Now, let's give service worker a job to do. When the service worker is first installed, we'll tell it to download and save all the essential files that make up our app's user interface—the "app shell."&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;sw.js&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const CACHE_NAME = 'app-shell-cache-v1';
const ASSETS_TO_CACHE = [
  '/',
  '/index.html',
  '/styles.css', // &amp;lt;-- your main CSS file
  '/app.js',     // &amp;lt;-- your main JS file/entry point
  '/some-other-chunk.js', // other JS files your app needs
  '/favicon.ico',
  '/images/icon-192.png' // &amp;lt;-- remember your own icon file in your images folder
];

// the install event fires when the service worker is first installed
self.addEventListener('install', event =&amp;gt; {
  console.log('service worker: installing...');

  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache =&amp;gt; {
        console.log('service worker: caching app shell');
        return cache.addAll(ASSETS_TO_CACHE);
      })
  );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we define a list of files that are critical for our UI. The &lt;code&gt;install&lt;/code&gt; event opens a cache with a specific name and saves those files for later. Modern JavaScript tools (like Vite or Create React App) often create multiple JS and CSS files. You must cache all the essential ones for your app to work offline. Use the &lt;strong&gt;Network&lt;/strong&gt; tab in your browser's DevTools to see all the files your page requests on load and add them to this list. It's a bit tedious, but it will make your app work perfectly offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Intercepting Requests and Serving from Cache
&lt;/h2&gt;

&lt;p&gt;With our files cached, the final step is to tell the service worker to actually use the cache. We'll listen for any network request (&lt;code&gt;fetch&lt;/code&gt; event) made by our page. If the requested file is present in cache, it'll be served directly from there, completely bypassing the network.&lt;/p&gt;

&lt;p&gt;Add this code to the bottom of your &lt;code&gt;sw.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sw.js (continued)

// the activate event fires after the install event, good place to clean up old caches
self.addEventListener('activate', event =&amp;gt; {
    console.log('service worker: activating...');
    event.waitUntil(
        caches.keys().then(cacheNames =&amp;gt; {
            return Promise.all(
                cacheNames.map(cache =&amp;gt; {
                    if (cache !== CACHE_NAME) {
                        console.log('service worker: clearing old cache');
                        return caches.delete(cache);
                    }
                })
            );
        })
    );
});


// the fetch event fires for every network request
self.addEventListener('fetch', event =&amp;gt; {
  console.log('service worker: fetching');

  event.respondWith(
    caches.match(event.request)
      .then(response =&amp;gt; {
        // if the request is in the cache, return it, else fetch from the network
        return response || fetch(event.request);
      })
  );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This "cache-first" strategy is perfect for the app shell. Now, even if the user is offline, as long as they have visited the site once, the service worker will intercept the request for &lt;code&gt;index.html&lt;/code&gt; and serve it directly from the cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  STEP 5: Testing Your Offline-First App
&lt;/h2&gt;

&lt;p&gt;The best place to test this is in Chrome DevTools.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open DevTools (&lt;code&gt;Ctrl+Shift+I&lt;/code&gt; or &lt;code&gt;Cmd+Opt+I&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Application&lt;/strong&gt; tab.&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%2Fwccyia0ty98rchza8d51.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%2Fwccyia0ty98rchza8d51.png" alt="screenshot showing the 'Applications' option in Chrome DevTools" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under the &lt;strong&gt;Service Workers&lt;/strong&gt; section, you should see your &lt;code&gt;sw.js&lt;/code&gt; file is "activated and is running." You can first unregister any old service workers.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Cache Storage&lt;/strong&gt;, you can inspect your &lt;code&gt;app-shell-cache-v1&lt;/code&gt; and see all the files you saved. Feel free to delete your old cache.&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%2Fde7v0yidxizfmvbciucp.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%2Fde7v0yidxizfmvbciucp.png" alt="screenshot showing 'Cache Storage' section in the chrome DevTools" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the &lt;strong&gt;"Offline"&lt;/strong&gt; box at the top of the DevTools window and try refreshing your page. It should still load perfectly.&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%2F37azl97d606n0h9irnqo.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%2F37azl97d606n0h9irnqo.png" alt="Screenshot of the DevTools Application tab showing the registered Service Worker and the " width="800" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila😁! You've just levelled up your web app in a massive way. It's now faster, more reliable, and provides a native-like experience by being installable and working offline. These are techniques at the core of modern, high-quality web development.&lt;/p&gt;

&lt;p&gt;And this is just the beginning of your PWA journey. From here, you could explore caching dynamic API data with IndexedDB or engaging users with push notifications.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>frontend</category>
    </item>
    <item>
      <title>check out this tutorial on building your own full-stack AI web app to analyze travel images!</title>
      <dc:creator>taiwo</dc:creator>
      <pubDate>Thu, 28 Aug 2025 00:54:00 +0000</pubDate>
      <link>https://dev.to/taiwofamaks/check-out-this-tutorial-on-building-your-own-full-stack-ai-web-app-to-analyze-travel-images-300p</link>
      <guid>https://dev.to/taiwofamaks/check-out-this-tutorial-on-building-your-own-full-stack-ai-web-app-to-analyze-travel-images-300p</guid>
      <description>&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://dev.to/taiwofamaks/build-a-full-stack-travel-lens-ai-with-gemini-vertex-on-cloud-shell-1mjd" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpylplsag3av231x2lyc6.png" 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://dev.to/taiwofamaks/build-a-full-stack-travel-lens-ai-with-gemini-vertex-on-cloud-shell-1mjd" rel="noopener noreferrer" class="c-link"&gt;
            Architecting a Serverless Vision System with Gemini on Cloud Run - DEV Community
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Have you ever wanted to build an intelligent application that can see and understand images? Yes? Me...
          &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png"&gt;
          dev.to
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Architecting a Serverless Vision System with Gemini on Cloud Run</title>
      <dc:creator>taiwo</dc:creator>
      <pubDate>Thu, 28 Aug 2025 00:41:26 +0000</pubDate>
      <link>https://dev.to/taiwofamaks/build-a-full-stack-travel-lens-ai-with-gemini-vertex-on-cloud-shell-1mjd</link>
      <guid>https://dev.to/taiwofamaks/build-a-full-stack-travel-lens-ai-with-gemini-vertex-on-cloud-shell-1mjd</guid>
      <description>&lt;p&gt;Have you ever wanted to build an intelligent application that can see and &lt;em&gt;understand&lt;/em&gt; images? Yes? Me too! I was recently inspired by a fellow dev and created my own &lt;a href="//bit.ly/bwai-tlai"&gt;Colab Notebook&lt;/a&gt; for an AI-powered Travel Blogger Assistant. But then I thought, why stop at a notebook? What if we took this idea a step further and built a production-ready and serverless web application that anyone could actually use?&lt;/p&gt;

&lt;p&gt;That's exactly what we're going to do! We'll transform the core logic from my notebook into &lt;strong&gt;Travel Lens AI&lt;/strong&gt;; a real web app that analyzes your travel photos, identifies landmarks, and gives you personalized recommendations, all powered by Google's Gemini model.&lt;/p&gt;

&lt;p&gt;In this guide, we aren't just coding; we are architecting. We will use Google Cloud Shell as our development sandbox and then transition to &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Google Cloud Run&lt;/a&gt; for production-grade deployment. By using a serverless approach, we ensure our app is highly scalable, secure, and cost-efficient (scaling to zero when not in use).&lt;/p&gt;

&lt;p&gt;It was a blast creating this, and now I want to show you how to take an AI concept from a notebook to a live, cloud-native application; all without leaving your browser!&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, you'll have a working full-stack application deployed globally. This project demonstrates serverless orchestration, AI integration, and production deployment. Perfect for your portfolio and your journey toward becoming a Cloud expert.&lt;/p&gt;

&lt;p&gt;Let's cook!👩🏼‍🍳&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting Up Your Google Cloud Environment
&lt;/h2&gt;

&lt;p&gt;We need a place to build our app. We'll use Google Cloud's free tier. (You have a Google account, obviously.) This gives you everything you need to build this app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create or Select a Project.
&lt;/h3&gt;

&lt;p&gt;Head over to the &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;. Create a new project (you can name it travel-lens-ai) or select an existing one.&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%2Fpdfded5pcaql4t1ld285.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%2Fpdfded5pcaql4t1ld285.png" alt="screenshot from cloud console showing the projects section" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable the Vertex AI API.
&lt;/h3&gt;

&lt;p&gt;This is the magic that gives us access to Gemini. In the search bar at the top, type &lt;strong&gt;Vertex AI API&lt;/strong&gt; and select it. Click the blue &lt;strong&gt;Enable&lt;/strong&gt; button. This might take a minute.&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%2Fwbw5qcmuko90pyhlj2m1.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%2Fwbw5qcmuko90pyhlj2m1.png" alt="screenshot from cloud console showing the " width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Link Billing.
&lt;/h3&gt;

&lt;p&gt;Don't worry. Even though Google requires a billing account for API access (and spam prevention), you won't be charged anything for this project. You can use an existing billing account or create a new one to access the generous free trial credits. Just head to the Billing menu on the sidebar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Launch Cloud Shell.
&lt;/h3&gt;

&lt;p&gt;In the top right corner of the console, click the &lt;strong&gt;&amp;gt;_&lt;/strong&gt; icon to Activate Cloud Shell. Once it's running, click the &lt;strong&gt;Open Editor&lt;/strong&gt; button to launch the VS Code-like editor in your browser.&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%2Fmw8yyibzdcoauvw65g8c.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%2Fmw8yyibzdcoauvw65g8c.png" alt="screenshot from cloud console highlighting the shell and " width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An "Authorize Cloud Shell" prompt might pop up, please do not reject haha&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Creating the Project Structure
&lt;/h2&gt;

&lt;p&gt;Now that we're in the editor, let's set up our project structure. You'll see the Cloud Shell terminal at the bottom of the editor. If it's not visible, just hit &lt;code&gt;ctrl/cmd + J&lt;/code&gt; to toggle it open. Run these commands one by one to create our files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create the main project folder and navigate into it
mkdir travel-lens-ai &amp;amp;&amp;amp; cd travel-lens-ai

# Create our Python backend file
touch app.py

# Create a file to list our Python libraries
touch requirements.txt

# Create a folder for our HTML file
mkdir templates &amp;amp;&amp;amp; touch templates/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your file explorer on the left should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmivu4d7sm8gw8x2txvb.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%2Frmivu4d7sm8gw8x2txvb.png" alt="screenshot of file explorer from code editor" width="370" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Building the Python Backend with Flask
&lt;/h2&gt;

&lt;p&gt;Our backend will be a simple web server using Flask. It will have one job: to receive an image, send it to the Gemini model for analysis, and return the results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define Dependencies.
&lt;/h3&gt;

&lt;p&gt;Open the &lt;code&gt;requirements.txt&lt;/code&gt; file and add the libraries our app needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flask
flask-cors
google-cloud-aiplatform
Pillow
google-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install the Libraries.
&lt;/h3&gt;

&lt;p&gt;In the terminal, run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write the Flask App.
&lt;/h3&gt;

&lt;p&gt;Now, open &lt;code&gt;app.py&lt;/code&gt; and paste the following code into it. This code sets up the server, defines the AI analysis logic, creates the API endpoint and sets the frontend route.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up &amp;amp; initializing
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app.py
from flask import Flask, request, jsonify, render_template
from flask_cors import CORS
import vertexai
from vertexai.generative_models import GenerativeModel, Part
import os
import google.auth

# Initialize the Flask application
app = Flask(__name__)
CORS(app)

# --- Configuration ---
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # Allow up to 16MB images

PROJECT_ID = "travel-lens-ai"  # &amp;lt;-- IMPORTANT: REPLACE WITH YOUR PROJECT ID
LOCATION = "us-central1"
MODEL_NAME = "gemini-2.0-flash-exp"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Core AI analysis logic
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def analyze_image_from_bytes(image_bytes):
    try:
        credentials, project_id = google.auth.default()
        vertexai.init(project=PROJECT_ID, location=LOCATION, credentials=credentials)

        multimodal_model = GenerativeModel(MODEL_NAME)
        image_part = Part.from_data(image_bytes, mime_type="image/jpeg")

# --- Prompting. You can customize to how you want it ---
        prompt = """
        You are a travel expert. Analyze the image provided and provide the following information in a JSON format:
        - "landmarkName": The name of the landmark, city, or place.
        - "description": A captivating and interesting description of the place.
        - "location": A JSON object with "city" and "country".
        - "personalizedRecommendations": An array of 3-4 actionable travel tips.
        - "photoTips": A creative tip for taking a great photo at this location.
        """

        response = multimodal_model.generate_content([prompt, image_part])
        response_text = response.text.strip().replace("```

json", "").replace("

```", "")

        return json.loads(response_text)

    except Exception as e:
        print(f"An error occurred during AI analysis: {e}")
        return {"error": f"An internal AI error occurred. Details: {e}"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;API endpoint &amp;amp; frontend route
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.route('/analyze', methods=['POST'])
def analyze_image_endpoint():
    if 'image' not in request.files:
        return jsonify({"error": "No image file provided"}), 400

    image_file = request.files['image']
    analysis_result = analyze_image_from_bytes(image_file.read())

    if "error" in analysis_result:
        return jsonify(analysis_result), 500

    return jsonify(analysis_result), 200

# --- Frontend Route ---
@app.route('/')
def home():
    return render_template('index.html')  # &amp;lt;-- IMPORTANT: remember our setup in the beginning

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)), debug=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Fear not! You're not just blindly copying a long a** code blocks. I have broken it down with comments so you understand exactly what each section does. This way, you're actually learning, not just following instructions. Remember to replace &lt;code&gt;YOUR_PROJECT_ID&lt;/code&gt; with your actual Google Cloud Project ID (unless you're using &lt;code&gt;travel-lens-ai&lt;/code&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Creating the UI
&lt;/h2&gt;

&lt;p&gt;Now the fun part. Let's build the user interface. This will be a single HTML file that uses Tailwind CSS for styling and JavaScript to connect with our backend.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;templates/index.html&lt;/code&gt; and you can either build your own custom interface or use &lt;a href="https://github.com/taiwocodes/travel-lens-ai/blob/main/templates/index.html" rel="noopener noreferrer"&gt;my implementation&lt;/a&gt; as a starting point. The key elements you'll need are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;styled file input for uploading travel photos&lt;/li&gt;
&lt;li&gt;submit button to trigger the AI analysis&lt;/li&gt;
&lt;li&gt;results section to display Gemini's insights&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you customize it or use my code as-is, make sure you understand the JavaScript, as that's what sends your images to the AI for analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Preview Your Application
&lt;/h2&gt;

&lt;p&gt;This is the moment we've been waiting for. With our backend logic and frontend interface in place, it's time to bring Travel Lens AI to life.&lt;br&gt;
In the Cloud Shell terminal, (again, make sure you are in your &lt;code&gt;travel-lens-ai&lt;/code&gt;) directory and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a few seconds, the terminal will show that the Flask server is running. Now, look at the top-right corner of the Cloud Shell window for a &lt;strong&gt;Web Preview&lt;/strong&gt; button. Click it and select &lt;strong&gt;Preview on port 8080&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.amazonaws.com%2Fuploads%2Farticles%2Fwrc8gchildzfdligm9zg.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%2Fwrc8gchildzfdligm9zg.png" alt="screenshot showing " width="640" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new browser tab will open, confirming your application loads correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Deploy to Cloud Run!🚀
&lt;/h2&gt;

&lt;p&gt;Now that we’ve verified our app works in the Cloud Shell sandbox, the final step is to transition to a production-grade environment. We’ll use Google Cloud Run, a fully managed serverless platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Cloud Run?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elastic Scalability: It automatically scales your container up to handle traffic and down to zero when idle.&lt;/li&gt;
&lt;li&gt;Cost-Efficiency: You only pay for the exact resources used during request processing.
*Infrastructure as Code: It allows us to move away from manual 'previews' to a stable, public HTTPS URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enable Required APIs
&lt;/h3&gt;

&lt;p&gt;Before deploying, we need to enable the Cloud Build and Artifact Registry APIs. These services handle the "magic" of turning your source code into a deployable container image. Run this command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud services enable run.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Production Launch
&lt;/h3&gt;

&lt;p&gt;Deploy your application directly from your current directory with this command. When prompted to "allow unauthenticated invocations," type y to make your travel app public!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud run deploy travel-lens-ai \
  --source . \
  --region=us-central1 \
  --allow-unauthenticated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  And we are live!🥳
&lt;/h3&gt;

&lt;p&gt;Once the deployment finishes (it takes about 2 minutes), the terminal will display a Service URL. This is your permanent, production link! Go ahead and upload a photo of your favorite travel spot and watch Gemini work its magic!✨&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%2Fifz5v2hswlkuzswbmisa.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%2Fifz5v2hswlkuzswbmisa.png" alt="screenshot showing the frontend of Travel Lens AI" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting Checklist
&lt;/h2&gt;

&lt;p&gt;If you run into an error, don't panic! Here are the most common fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Permissions Error? The most common issue is authentication. Force Cloud Shell to re-authenticate by running &lt;code&gt;gcloud auth application-default login&lt;/code&gt; in the terminal, follow the link to authorize, and then restart your &lt;code&gt;python app.py&lt;/code&gt; server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Project Not Found"? Make sure the &lt;code&gt;PROJECT_ID&lt;/code&gt; in your &lt;code&gt;app.py&lt;/code&gt; file exactly matches the ID of the project where you enabled the Vertex AI API. You can also reconfirm the project in Cloud Shell by running &lt;code&gt;gcloud config set project PROJECT_ID&lt;/code&gt; in your terminal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dependencies Missing? If you see an "import error," you may have forgotten to install the libraries. Just run &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you accidentally close your terminal or clear the screen and need to find the URL again, you don't need to re-deploy. Just run this command in Cloud Shell:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud run services describe travel-lens-ai --format='value(status.url)' --region=us-central1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will print only the live URL to your screen.&lt;/p&gt;

&lt;p&gt;Congratulations!🥳 You've just built and deployed a powerful, full-stack AI application using nothing but your web browser and cloud run. How cool is that?!&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zjlp5qb3u7sp50pd79q.gif" 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%2F0zjlp5qb3u7sp50pd79q.gif" alt="gif of anime characters Killua and Gon from Hunter X Hunter celebrating" width="480" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You've learned how to set up a cloud environment, build a Python backend, create a modern frontend, integrate with the powerful Gemini model, and deploy on Cloud Run.&lt;/p&gt;

&lt;p&gt;But this is just the beginning. You could expand this project by saving analysis history, adding user accounts, or even generating entire travel itineraries.&lt;/p&gt;

&lt;p&gt;A huge shoutout to the dev whose &lt;a href="https://colab.research.google.com/drive/1mdg7REuE2ft-2BDfcSsWnaVhOAJJUvpB?usp=sharing#scrollTo=5LiXOm359Cq7" rel="noopener noreferrer"&gt;Colab Notebook&lt;/a&gt; sparked this entire project! It's incredible how inspiration can transform a simple notebook into a full-stack application. That's the beauty of open development and sharing knowledge.&lt;/p&gt;

&lt;p&gt;I hope this tutorial has shown you how simple(👀), accessible and powerful modern AI development can be. Happy hacking!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update (Jan 2026): Added a new section on production deployment using Google Cloud Run to demonstrate how to transition this AI workflow from a development sandbox to a scalable, serverless environment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Behind The Scenes: How Your Browser Cooks Up a Webpage.</title>
      <dc:creator>taiwo</dc:creator>
      <pubDate>Thu, 08 Aug 2024 19:25:10 +0000</pubDate>
      <link>https://dev.to/taiwofamaks/behind-the-scenes-how-your-browser-cooks-up-a-webpage-3kka</link>
      <guid>https://dev.to/taiwofamaks/behind-the-scenes-how-your-browser-cooks-up-a-webpage-3kka</guid>
      <description>&lt;p&gt;Every second, billions of webpages spring to life. It’s a digital feast happening at breakneck speed. But what truly goes on behind the scenes when you simply type a URL into your browser? Seriously, like a million things.&lt;/p&gt;

&lt;p&gt;Think of the internet as a colossal, global kitchen. Countless servers, like dedicated chefs, work tirelessly to prepare an endless menu of digital dishes. When you enter a website address in your browser, you are essentially placing an order at this grand culinary establishment.&lt;/p&gt;

&lt;p&gt;To understand how your digital meal is prepared and served, let's step into the kitchen.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Order is Placed (Typing the URL)
&lt;/h3&gt;

&lt;p&gt;Just as you place your order with the waiter, you tell your browser the information you need by typing a URL (Uniform Resource Locator) into the address bar. This unique identifier, often starting with &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;, is like the dish name on a menu. It tells the waiter the exact meal you want, for them to take to the chefs.&lt;/p&gt;

&lt;p&gt;Your browser (Chrome, Safari, Edge, etc) acts as your personal waiter, taking your order with precision. When you hit enter, it diligently records the URL, storing it as a specific request. Imagine a knowledgeable and savvy waiter who understands the complexities of the restaurant (the internet) and knows how to relay your order to the appropriate kitchen (server). That's your browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding the Right Kitchen (DNS Lookup)
&lt;/h3&gt;

&lt;p&gt;While your waiter (browser) has taken your order (URL), they don't actually know where the kitchen (server) is located. This is where the magic of the Domain Name System (DNS) comes in. Think of DNS servers as the restaurant's kitchen directory.&lt;/p&gt;

&lt;p&gt;Now you don't expect your waiter to wander aimlessly through the restaurant looking for the kitchen. Remember your waiter is knowledgeable and savvy. So instead of searching the entire restaurant for the kitchen, they consult the directory (DNS server) to quickly locate the right kitchen to send your order. This process, called a DNS lookup, happens in milliseconds, ensuring your meal, or webpage, is on its way swiftly.&lt;/p&gt;

&lt;p&gt;This is how your browser sends your URL to a DNS server. This server acts as a translator, converting the human-readable URL into a numerical address called an IP address which looks like this &lt;code&gt;192.122.56.10&lt;/code&gt;. Once the browser receives the IP address from the DNS server, it knows exactly where to send your order. The kitchen, or in this case the server, is ready to start cooking up your webpage.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Chef Prepares the Food (Server Processing)
&lt;/h3&gt;

&lt;p&gt;Once your browser, acting as the diligent waiter, successfully locates the kitchen (server) using the IP address, it sends your order to the digital chef. This is where the magic of server-side processing begins.&lt;/p&gt;

&lt;p&gt;Like a bustling kitchen with different chefs specializing in different dishes, there are different types of servers handling various requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Web Servers: These are the main chefs. They receive your request, fetch the requested webpage from the server's storage, and prepare it for serving.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Database Servers: These are the sous-chefs. They manage and store information like product details, user data, or blog posts. Web servers often interact with database servers to retrieve dynamic content.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mail Servers: These are the delivery experts, handling email-related tasks like sending, receiving, and storing emails.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The server processes your request based on the information it receives. This involves tasks like checking if the requested page exists, gathering necessary data (if required), and generating the HTML code that makes up the webpage. Think of this as the chef combining ingredients and cooking the dish according to the recipe.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Food is Served (Page Loading)
&lt;/h3&gt;

&lt;p&gt;With the webpage fetched from the server, it's time for delivery. Just as the waiter carries your plate of delicious food to your table, the server sends the webpage, packaged as data, back to your browser. This transfer happens at incredible speed, thanks to the internet's infrastructure.&lt;/p&gt;

&lt;p&gt;Once the browser receives the data, it starts the process of presenting it to you in a visually appealing format. This is like the waiter setting the table and arranging the dishes. The browser interprets the HTML code, which is the blueprint of the webpage, and starts building the structure. It then incorporates elements like CSS (Cascading Style Sheets) for styling and JavaScript for interactivity.&lt;/p&gt;

&lt;p&gt;Again, remember that your browser as a skilled waiter who not only brings the food but also ensures it's presented elegantly. It arranges text, images, and other components in a way that is easy to understand and navigate. The result is the dynamic and interactive webpage you see on your screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extra Sauce
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Caching: Speeding Things Up
&lt;/h4&gt;

&lt;p&gt;To enhance the dining experience, restaurants often keep popular dishes warm to reduce wait times. Similarly, browsers employ caching to accelerate webpage loading. When you visit a website, your browser saves a copy, or cache, of the webpage's files (HTML, images, scripts) on your computer. The next time you visit the same page, the browser checks the cache first. If the files haven't changed, they're loaded directly from your computer, bypassing the server. This significantly speeds up page loading, especially for frequently visited websites.&lt;/p&gt;

&lt;h4&gt;
  
  
  Security: Protecting Your Digital Meal
&lt;/h4&gt;

&lt;p&gt;Just as restaurants prioritize food safety, the internet emphasizes data security. HTTPS (Hypertext Transfer Protocol Secure) is the digital equivalent of a locked food container in the kitchen. It ensures that the data exchanged between your browser and the server remains private and intact.&lt;/p&gt;

&lt;p&gt;When you access a website with HTTPS, your browser establishes a secure connection with the server. This connection encrypts the data, making it unreadable to anyone who might intercept it. Think of it as wrapping your food order in a secure package before sending it. HTTPS also verifies the website's identity, preventing you from being tricked into visiting a fake restaurant.&lt;/p&gt;

&lt;h4&gt;
  
  
  Troubleshooting: Dealing with Digital Hiccups
&lt;/h4&gt;

&lt;p&gt;Even the smoothest dining experiences can encounter occasional hiccups, like a slow waiter or cold food. Similarly, webpage loading can sometimes be met with challenges. Here are some common issues and potential solutions:&lt;/p&gt;

&lt;h5&gt;
  
  
  Slow Loading Times:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Check your internet connection speed.&lt;/li&gt;
&lt;li&gt;Close unnecessary tabs and programs.&lt;/li&gt;
&lt;li&gt;Clear your browser cache.&lt;/li&gt;
&lt;li&gt;Consider using a content delivery network (CDN).&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Webpage Errors:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Ensure the URL is correct.&lt;/li&gt;
&lt;li&gt;Try accessing the website from a different device or network.&lt;/li&gt;
&lt;li&gt;Check if the website is experiencing technical difficulties.&lt;/li&gt;
&lt;li&gt;Clear your browser cookies and cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Security Warnings:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Avoid clicking on suspicious links.&lt;/li&gt;
&lt;li&gt;Be cautious of phishing attempts.&lt;/li&gt;
&lt;li&gt;Keep your browser and operating system up to date.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bon Appétit!
&lt;/h3&gt;

&lt;p&gt;While we've merely scratched the surface of this intricate world of web development, grasping these fundamental concepts can make you a more informed and efficient web explorer, help you to diagnose online troubles, admire the engineering behind your favorite websites, and even spark groundbreaking ideas. So, as you savor your perfectly cooked meal, do well to appreciate the skilled chefs and efficient waitstaff who made it possible.&lt;/p&gt;

&lt;p&gt;Happy browsing!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
      <category>learning</category>
    </item>
    <item>
      <title>React Components: Class vs Functional.</title>
      <dc:creator>taiwo</dc:creator>
      <pubDate>Wed, 31 Jul 2024 21:56:35 +0000</pubDate>
      <link>https://dev.to/taiwofamaks/react-components-class-vs-functional-5ebm</link>
      <guid>https://dev.to/taiwofamaks/react-components-class-vs-functional-5ebm</guid>
      <description>&lt;p&gt;My React journey began four years ago with functional components and Hooks. Then came 'Siswe, a fellow participant in the bootcamp and our resident class component enthusiast. While the rest of us were collaborating on team projects with functional components, 'Siswe clung to class components with an unwavering loyalty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Components are the building blocks of your user interface (UI).
&lt;/h3&gt;

&lt;p&gt;Think of them as Lego bricks – you can combine them in various ways to create complex structures. They are independent and reusable pieces of code that encapsulate UI and logic.&lt;/p&gt;

&lt;p&gt;Reusing a component within another component typically looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import MyComponent from './MyComponent';

function ParentComponent() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;MyComponent /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Class Components and Functional Components are the two primary ways to create components in React.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from 'react';

class Counter extends Component {
 constructor(props) {
  super(props);
  this.state = { count: 0 };
 }

 handleClick = () =&amp;gt; {
  this.setState({  
 count: this.state.count + 1 });
 };

 render() {
  return  
 (
   &amp;lt;div&amp;gt;
    &amp;lt;p&amp;gt;You clicked {this.state.count} times&amp;lt;/p&amp;gt;
    &amp;lt;button onClick={this.handleClick}&amp;gt;Click me&amp;lt;/button&amp;gt;
   &amp;lt;/div&amp;gt;
  );
 }
}

export default Counter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a class component, created using JavaScript classes that extend the &lt;code&gt;React.Component&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const handleClick = () =&amp;gt; {
    setCount(count + 1);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;You clicked {count} times&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;Click me&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Counter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This on the other hand is a functional component, written as a simple JavaScript function.&lt;/p&gt;

&lt;h3&gt;
  
  
  State Management: The Core Difference.
&lt;/h3&gt;

&lt;p&gt;Class components manage their own internal state using &lt;code&gt;this.state&lt;/code&gt;. This is typically initialized in the constructor, accessed using &lt;code&gt;this.state&lt;/code&gt; object, and updated using the &lt;code&gt;this.setState&lt;/code&gt; method, as seen in the code block above.&lt;/p&gt;

&lt;p&gt;Functional components were initially stateless. But with the introduction of Hooks, they gained the ability to manage state and lifecycle logic. Utilizing the &lt;code&gt;useState&lt;/code&gt; hook for managing state, it returns a pair of values: the current state and a function to update it, as seen above. This is sufficient for simple state management. For more complex state logic involving multiple sub-values, or when the next state depends on the previous one, you want to use &lt;code&gt;useReducer&lt;/code&gt;.&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useReducer } from 'react';

const initialState = {
  count: 0,
  step: 1,
};

const reducer = (state, action) =&amp;gt; {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + state.step };
    case 'decrement':   

      return { ...state, count: state.count - state.step };
    case 'setStep':
      return { ...state, step: action.payload   
 };
    default:
      throw new Error();
  }
};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const increment = () =&amp;gt; dispatch({ type: 'increment' });
  const decrement = () =&amp;gt; dispatch({ type: 'decrement'   
 });
  const setStep = (newStep) =&amp;gt; dispatch({ type: 'setStep', payload: newStep });

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Count: {state.count}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Step: {state.step}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;+&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={decrement}&amp;gt;-&amp;lt;/button&amp;gt;
      &amp;lt;input type="number" value={state.step} onChange={(e) =&amp;gt; setStep(Number(e.target.value))} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Counter;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;useReducer&lt;/code&gt; is managing multiple state values and complex update logic in a structured and maintainable way. Hooks are exclusively for functional components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid direct manipulation of the state object in both components.
&lt;/h3&gt;

&lt;p&gt;Never directly modify or mutate the state object, regardless of the component type. Instead, create a new object with the updated values. This approach helps React efficiently track changes and optimize re-renders.&lt;/p&gt;

&lt;p&gt;Functional component example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';

function UserProfile() {
  const [user, setUser] = useState({ name: 'Jane Doe', age: 30 });

  const handleNameChange = (newName) =&amp;gt; {
    setUser({ ...user, name: newName }); // Create a new object with updated name
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Name: {user.name}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Age: {user.age}&amp;lt;/p&amp;gt;
      &amp;lt;input type="text" value={user.name} onChange={(e) =&amp;gt; handleNameChange(e.target.value)} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default UserProfile;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Class component example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from 'react';

class UserProfile extends Component {
  state = { user: { name: 'Jane Doe', age: 30 } };

  handleNameChange = (newName) =&amp;gt; {
    this.setState(prevState =&amp;gt; ({
      user: { ...prevState.user, name: newName } // Create a new object with updated name
    }));
  };

  render() {
    return (
      &amp;lt;div&amp;gt;
        &amp;lt;p&amp;gt;Name: {this.state.user.name}&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;Age: {this.state.user.age}&amp;lt;/p&amp;gt;
        &amp;lt;input type="text" value={this.state.user.name} onChange={(e) =&amp;gt; this.handleNameChange(e.target.value)} /&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}

export default UserProfile;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both examples, we're updating the &lt;code&gt;name&lt;/code&gt; property of the &lt;code&gt;user&lt;/code&gt; object while preserving the original object's integrity. This ensures that a new state object is created, preserving immutability and preventing potential issues with state updates. Adherence to this ensures predictable behavior, performance optimizations, and easier debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class components are for complex logic.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex State Management:&lt;/strong&gt; When dealing with intricate state logic that requires fine-grained control, class components with &lt;code&gt;this.state&lt;/code&gt; and &lt;code&gt;this.setState&lt;/code&gt; can offer more flexibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle Methods:&lt;/strong&gt; For components that heavily rely on lifecycle methods like &lt;code&gt;componentDidMount&lt;/code&gt;, &lt;code&gt;componentDidUpdate&lt;/code&gt;, or &lt;code&gt;componentWillUnmount&lt;/code&gt;, class components are the traditional choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Boundaries:&lt;/strong&gt; To handle errors within a component tree and prevent crashes, class components with &lt;code&gt;componentDidCatch&lt;/code&gt; are essential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Optimization:&lt;/strong&gt; In specific performance-critical scenarios, &lt;code&gt;PureComponent&lt;/code&gt; or &lt;code&gt;shouldComponentUpdate&lt;/code&gt; within class components can be leveraged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legacy Codebases:&lt;/strong&gt; If you're working on an existing project that heavily relies on class components, it might be easier to maintain consistency by using them for new components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Functional components are for simple views.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple Components:&lt;/strong&gt; For presentational components with minimal state or logic, functional components are often the preferred choice due to their simplicity and readability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management with Hooks:&lt;/strong&gt; Leveraging &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useReducer&lt;/code&gt; in functional components provides a powerful and flexible way to manage state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Side Effects:&lt;/strong&gt; The &lt;code&gt;useEffect&lt;/code&gt; hook allows for managing side effects like data fetching, subscriptions, or manual DOM (document object model) manipulations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Optimization:&lt;/strong&gt; &lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt; can be used to optimize performance in functional components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Let your component's needs guide your decision.
&lt;/h3&gt;

&lt;p&gt;The functional approach is generally considered more concise and readable, and it often suffices due to simplicity and efficiency. However, class components offer more control over state management and lifecycle methods, especially when dealing with intricate logic or performance optimization. This means better structure for organizing complex logic.&lt;/p&gt;

&lt;p&gt;The choice between class and functional components is not always clear-cut, as there is no strict rule. Evaluate the requirements of your component and go with the type that aligns best with your project requirements.&lt;/p&gt;

&lt;p&gt;Which component do you enjoy working with more?&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Coding For Everyone: Accessibility in Web Development.</title>
      <dc:creator>taiwo</dc:creator>
      <pubDate>Tue, 30 Jul 2024 00:41:03 +0000</pubDate>
      <link>https://dev.to/taiwofamaks/coding-for-everyone-accessibility-in-web-development-181c</link>
      <guid>https://dev.to/taiwofamaks/coding-for-everyone-accessibility-in-web-development-181c</guid>
      <description>&lt;p&gt;The next billion users of the internet will come from a diverse range of backgrounds and abilities. Therefore, incorporating inclusive design principles from the outset of web development is extremely important. Disabilities that affect access to the web such as auditory, cognitive, neurological, physical, speech, and visual, are all encompassed by web accessibility. Websites, tools and technologies must be designed and developed so that people with disabilities can use them. Everyone should be able to perceive, understand, navigate, interact with and contribute to information presented on the web.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disabilities have impact on web accessibility.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Visual impairments.&lt;/strong&gt;
People with visual impairments such as partial or total blindness, color blindness and low vision rely heavily on screen readers and other assistive technologies to access the web. To accommodate their needs, websites must provide clear and concise alternative (alt) text for images, videos, and other non-text content; ensure sufficient color contrast between text and background for readability; utilize headings and proper HTML structure to create a logical document outline for screen readers; and allow users to adjust font size for optimal viewing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hearing impairments.&lt;/strong&gt;
Users with hearing impairments like deafness benefit from visual cues and transcripts. Websites must provide closed captions (CC) and transcripts for audio and video content; use visual indicators for audio notifications and alerts; and ensure compatibility with hearing aids and assistive listening devices.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Motor impairments.&lt;/strong&gt;
Individuals with motor impairments may have difficulty using their keyboards or mouse. To accommodate their needs, websites must implement keyboard navigation; provide sufficient target sizes for clickable elements; and allow users to control the website using voice commands or other assistive technologies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognitive impairments.&lt;/strong&gt;
People with cognitive impairments may have difficulty processing information, understanding complex layouts, or focusing on tasks. To create inclusive experiences, websites must use clear and simple language, avoiding complex sentence structures; provide clear and consistent navigation with well-labeled links and menus; minimize distractions and clutter on the page; and offer options for customizing the display such as font size, color, and layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users with other disabilities, such as speech impairments and seizure disorders can be catered to by websites providing options for text input and alternative communication methods, and avoiding flashing animations or content that could trigger seizures, respectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  It begins with us, the developers.
&lt;/h3&gt;

&lt;p&gt;Developers build the website. By incorporating accessibility principles from the foundation, we ensure that the web is inclusive and usable for everyone, regardless of disabilities. Our role is crucial, as we are responsible for implementing features like proper semantic HTML, keyboard navigation, and ARIA landmarks that make a significant difference in the user experience for people with disabilities. By prioritizing accessibility, we create a more inclusive digital environment that benefits all users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
— Tim Berners-Lee, inventor of the World Wide Web.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://www.clickawaypound.com/cap16finalreport.html" rel="noopener noreferrer"&gt;Click-Away Pound survey&lt;/a&gt; reports that 71% of users with disabilities will leave a website that is difficult to use, with 82% of those users willing to spend their money on a more accessible website. This goes to show that improving accessibility can lead to higher conversion rates as users are more likely to stay on and interact with a website that is easy to use. Also, accessible websites often rank higher in search engine results, driving more organic traffic. Features like alt text for images, proper use of headings, and descriptive link text enhance search engine optimization (SEO). Clear navigation, better contrast, and keyboard functionality also improve overall user experience, making it easier for everyone to use websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Netflix is a perfect case study.
&lt;/h3&gt;

&lt;p&gt;As a leading streaming service, Netflix understood the need to cater to a diverse audience, including those with visual and hearing impairments. They aimed to create an inclusive platform where everyone could enjoy their content and implemented closed captioning and audio descriptions for their content. They also developed features like customizable text size, font, and background color to accommodate users with visual impairments. By prioritizing accessibility, Netflix expanded its audience and strengthened its brand reputation as an inclusive company. They received positive feedback from users with disabilities and this demonstrated their commitment to creating a barrier-free viewing experience for everyone.&lt;/p&gt;

&lt;h3&gt;
  
  
  The common barriers are often overlooked.
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Images.&lt;/strong&gt;&lt;br&gt;
Making websites accessible starts with something as ‘little’ as adding descriptive alt texts that explain non-text content such as images and videos. Users with visual impairments rely on screen readers to describe these elements and without a descriptive alt text, they miss out on crucial information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;img src='cat.jpg' alt='a cat'/&amp;gt;

&amp;lt;img src='cat.jpg' alt='an orange cat sleeping on a couch'/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users relying on screen readers are more likely to navigate away from a website with vague alt text like the first example, as it is not descriptive enough. They will have a better experience navigating a website with alt texts similar to the latter, as detailed descriptions provide a richer user experience for those using assistive technologies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headings.&lt;/strong&gt;&lt;br&gt;
Using headings correctly establishes the document structure, helping users with screen readers understand the content hierarchy. Do not do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Important Content&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Less Important Content&amp;lt;/h2&amp;gt;
&amp;lt;h1&amp;gt;Even Less Important Content&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Main Heading&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Subheading 1&amp;lt;/h2&amp;gt;
&amp;lt;h3&amp;gt;Subheading 2&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tables.&lt;/strong&gt;&lt;br&gt;
This table structure cannot be easily navigated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;table&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;Column 1&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;Column 2&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
  &amp;lt;tr&amp;gt;
    &amp;lt;td&amp;gt;Data 1&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;Data 2&amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implement this structure instead, using &lt;em&gt;caption, thead, tbody&lt;/em&gt; and &lt;em&gt;th&lt;/em&gt; elements to provide structure, making your table accessible to screen reader users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;table&amp;gt;
  &amp;lt;caption&amp;gt;Table of Products&amp;lt;/caption&amp;gt;
  &amp;lt;thead&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;th&amp;gt;Product Name&amp;lt;/th&amp;gt;
      &amp;lt;th&amp;gt;Price&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;
  &amp;lt;tbody&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;Product A&amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;$10&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;Product B&amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;$15&amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ARIA roles.&lt;/strong&gt;&lt;br&gt;
Missing or incorrect use of ARIA (Accessible Rich Internet Applications) roles is another common barrier in web accessibility. If you can use a native HTML element with the correct semantics, do so, instead of using ARIA. ARIA should be used as a last resort for custom controls. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div role="button" onclick="doSomething()"&amp;gt;Click me!&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this code makes the element clickable, it is incorrect to use &lt;em&gt;role="button"&lt;/em&gt; on a generic &lt;em&gt;“div”&lt;/em&gt; element. Screen readers may not interpret the content correctly, leading to confusion and a poor user experience. The button element is specifically designed for clickable elements and provides built-in accessibility features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div role="alert" aria-live="assertive"&amp;gt;
  Error: Please fill in all required fields.
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;em&gt;role=&lt;/em&gt;“&lt;em&gt;alert”&lt;/em&gt; indicates that the content is important, while &lt;em&gt;aria-live="assertive"&lt;/em&gt; ensures that the alert is announced immediately to users of assistive technologies.&lt;/p&gt;

&lt;h3&gt;
  
  
  You can easily check the accessibility of your website.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://wave.webaim.org/" rel="noopener noreferrer"&gt;WAVE&lt;/a&gt;, &lt;a href="https://chromewebstore.google.com/detail/axe-devtools-web-accessib/lhdoppojpmngadmnindnejefpokejbdd" rel="noopener noreferrer"&gt;Axe&lt;/a&gt;, &lt;a href="https://pa11y.org/" rel="noopener noreferrer"&gt;Pa11y&lt;/a&gt;, and Lighthouse are some accessibility testing tools that can be used to audit your website and identify areas that may need improvement. Here are the steps to access and use the &lt;strong&gt;Lighthouse Panel&lt;/strong&gt; in your Chrome DevTools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Chrome DevTools. Right-click on your webpage and select &lt;em&gt;Inspect,&lt;/em&gt; or press &lt;em&gt;Ctrl+Shift+I&lt;/em&gt; (Windows/Linux) or &lt;em&gt;Cmd+Option+I&lt;/em&gt; (Mac).&lt;/li&gt;
&lt;li&gt;Navigate to the Lighthouse Panel. In the DevTools window, find and click the ‘&lt;em&gt;&amp;gt;&amp;gt;’&lt;/em&gt; icon at the top right to expand the available tabs. Select &lt;em&gt;Lighthouse&lt;/em&gt; from the dropdown menu.&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%2F1q83d1s3oyqefd2mvdbv.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%2F1q83d1s3oyqefd2mvdbv.png" alt="A screenshot showing how to navigate to ‘Lighthouse’ on the Chrome DevTools menu." width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set Up the Lighthouse Audit. In the Lighthouse panel, you can select the categories you want to audit. Make sure &lt;em&gt;Accessibility&lt;/em&gt; is checked. You can also choose the device type (Mobile or Desktop) for the audit.&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%2Fvjx80jhovmw0880savkr.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%2Fvjx80jhovmw0880savkr.png" alt="screenshot showing the lighthouse panel on chrome dev tools" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run the Audit. Click the &lt;em&gt;Analyze page load&lt;/em&gt; button at the top of the Lighthouse panel. Lighthouse will analyze your webpage in 30 to 60 seconds and generate a detailed report on the various categories checked, so ensure once again that &lt;em&gt;Accessibility&lt;/em&gt; is checked before you run the audit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Review the Accessibility Report. Once the audit is complete, Lighthouse will display a report with scores and detailed information on your website’s accessibility. The report will highlight areas that need improvement and provide suggestions on how to fix any issues.&lt;/p&gt;&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%2F0zdmzetmywjq7j8ji0tv.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%2F0zdmzetmywjq7j8ji0tv.png" alt="Screenshot showing performance result after Lighthouse audits a web page." width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more details, you can refer to the &lt;a href="https://developer.chrome.com/docs/lighthouse/overview" rel="noopener noreferrer"&gt;Chrome documentation on using Lighthouse.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility is a necessity, not an afterthought.
&lt;/h3&gt;

&lt;p&gt;It is crucial for true inclusion. The web should be usable by everyone, regardless of ability. We should be building bridges, not walls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Further Reading.
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/fundamentals/" rel="noopener noreferrer"&gt;Accessibility fundamentals.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.levelaccess.com/blog/understanding-assistive-technology-how-does-a-blind-person-use-the-internet/#:~:text=Much%20like%20how%20a%20sighted,same%20with%20their%20screen%20reader" rel="noopener noreferrer"&gt;Understanding Assistive Technology: How Does a Blind Person use the Internet?&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>a11y</category>
      <category>coding</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
