<?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>Forem: Art</title>
    <description>The latest articles on Forem by Art (@dailysandbox).</description>
    <link>https://forem.com/dailysandbox</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%2F2001002%2Ffc96459e-57f7-4e6f-9285-29ec0a2dae81.png</url>
      <title>Forem: Art</title>
      <link>https://forem.com/dailysandbox</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dailysandbox"/>
    <language>en</language>
    <item>
      <title>🚀 Build a Custom Scheduling Page in Minutes with Cogency.io (No Code Needed)</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Fri, 06 Jun 2025 21:44:12 +0000</pubDate>
      <link>https://forem.com/dailysandbox/build-a-custom-scheduling-page-in-minutes-with-cogencyio-no-code-needed-4fa8</link>
      <guid>https://forem.com/dailysandbox/build-a-custom-scheduling-page-in-minutes-with-cogencyio-no-code-needed-4fa8</guid>
      <description>&lt;p&gt;If you’ve been hunting for a clean, customizable way to let clients book time with you — without duct-taping tools together — Cogency.io might be your new favorite sidekick.&lt;/p&gt;

&lt;p&gt;In this quick-start guide, you’ll learn how to:&lt;/p&gt;

&lt;p&gt;✅ Set your availability&lt;br&gt;
✅ Build a 3-step booking flow (calendar → user info → confirmation)&lt;br&gt;
✅ Customize layouts with drag-and-drop (Pro &amp;amp; Teams)&lt;br&gt;
✅ Embed your page as a button, sticky bubble, or iframe&lt;br&gt;
✅ Tweak settings like meeting mode, duration, buffer, and even email branding&lt;br&gt;
✅ Add extra forms, generate QR codes, and more&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧑‍💻 No code required. Just clean UI and a powerful editor.&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.amazonaws.com%2Fuploads%2Farticles%2Fb06u08b06xkm0wvwjvnx.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%2Fb06u08b06xkm0wvwjvnx.png" alt="3-Page Scheduling Flow" width="800" height="955"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Findx659vne8ubpt1sul7.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%2Findx659vne8ubpt1sul7.gif" alt="Sticky Bubble in action" width="600" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whether you're a freelancer, coach, or startup founder, Cogency.io gives you pro-level scheduling without the dev time.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://cogency.io/kb/how-to-setup-a-scheduling-page" rel="noopener noreferrer"&gt;Check out the full tutorial here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-- Cogency Team&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Mind-Blowing CSS Trick: Coding Effect Without JS!</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Wed, 12 Feb 2025 03:06:16 +0000</pubDate>
      <link>https://forem.com/dailysandbox/mind-blowing-css-trick-coding-effect-without-js-2mib</link>
      <guid>https://forem.com/dailysandbox/mind-blowing-css-trick-coding-effect-without-js-2mib</guid>
      <description>&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%2Flqulk2r9ija0mmmbfvsf.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%2Flqulk2r9ija0mmmbfvsf.png" alt="Mind-Blowing CSS Trick: Coding Effect Without JS!" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yesterday, I stumbled upon an incredible code snippet that I couldn't keep to myself! It creates the illusion of typing code using nothing but pure CSS and HTML—no JavaScript required. Normally, I'd reach for JavaScript to achieve this effect, but this snippet proves that CSS alone can do the job brilliantly!&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%2F4wogemovt3y6w932j212.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%2F4wogemovt3y6w932j212.gif" alt="Mind-Blowing CSS Trick: Coding Effect Without JS!" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/klare/pen/xmgbaz?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;See the full code snippet here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Why Context Matters When Using AI for Programming</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Wed, 05 Feb 2025 03:30:04 +0000</pubDate>
      <link>https://forem.com/dailysandbox/why-context-matters-when-using-ai-for-programming-2cal</link>
      <guid>https://forem.com/dailysandbox/why-context-matters-when-using-ai-for-programming-2cal</guid>
      <description>&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%2Fja96ibrq30dz3qlywda5.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%2Fja96ibrq30dz3qlywda5.png" alt="Why Context Matters When Using AI for Programming" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI is an incredible tool for generating and fixing code, but it’s only as good as the context it’s given. When asking an AI to generate a function or fix one, the surrounding components of the system—such as data structures, APIs, dependencies, and external functions—are crucial to producing a correct solution.&lt;/p&gt;

&lt;p&gt;Without this context, AI has to make assumptions, leading to code that might look correct but fails when integrated. For example, if a function processes user input but the structure of that input isn’t provided, the AI might generate logic that doesn’t align with the actual data. Similarly, if a function depends on global state or API calls that aren’t included in the request, the AI won’t be able to account for them properly.&lt;/p&gt;

&lt;p&gt;Always provide relevant surrounding code and details. The more context AI has, the more accurate and useful its response will be—turning it from a guesswork generator into a true coding assistant.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>code</category>
    </item>
    <item>
      <title>Migrating from Bootstrap to Tailwind CSS</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Tue, 04 Feb 2025 03:57:17 +0000</pubDate>
      <link>https://forem.com/dailysandbox/migrating-from-bootstrap-to-tailwind-css-c7n</link>
      <guid>https://forem.com/dailysandbox/migrating-from-bootstrap-to-tailwind-css-c7n</guid>
      <description>&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%2Fgqja2m8gwe66qp8hyhv6.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%2Fgqja2m8gwe66qp8hyhv6.png" alt="Migrating from Bootstrap to Tailwind CSS" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apologies for the misleading title—while I set out to document my migration from Bootstrap 5 to Tailwind CSS, this post ultimately highlights another case where ChatGPT falls short.&lt;/p&gt;

&lt;p&gt;I started by asking ChatGPT for a step-by-step guide to migrate my project. As expected, it provided a seemingly straightforward plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# install tailwind
npm install -D tailwindcss postcss autoprefixer

# initialize tailwind
npx tailwindcss init -p

# configure the two files that were created
tailwind.config.js and postcss.config.js

# more steps to finilize the migration
(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, I immediately hit a roadblock on step two — &lt;em&gt;npx tailwindcss init -p&lt;/em&gt; threw an error:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;npm ERR! could not determine executable to run.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hoping for a quick fix, I pasted the error into ChatGPT, expecting a widely known solution. Instead, it repeatedly suggested checking if Tailwind was installed with &lt;code&gt;yarn list --pattern tailwindcss&lt;/code&gt; (it was) and reinstalling it. No matter how I reworded my query, it kept giving me variations of the same unhelpful answer.&lt;/p&gt;

&lt;p&gt;So, I took a different approach — I checked what &lt;em&gt;npx tailwindcss init -p&lt;/em&gt; was supposed to generate and manually created the config files. That worked, and I was able to continue the migration.&lt;/p&gt;

&lt;p&gt;ChatGPT is incredibly useful but still requires human intervention. It should have suggested alternative solutions, including manually creating the files. AI is powerful, but it’s not a replacement for problem-solving instincts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>css</category>
      <category>code</category>
    </item>
    <item>
      <title>Using Web Sockets to overcome Chrome Tab Freezing</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Tue, 21 Jan 2025 04:15:40 +0000</pubDate>
      <link>https://forem.com/dailysandbox/using-web-sockets-to-overcome-chrome-tab-freezing-120p</link>
      <guid>https://forem.com/dailysandbox/using-web-sockets-to-overcome-chrome-tab-freezing-120p</guid>
      <description>&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%2F4aef3rwb47kzyzjprhw6.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%2F4aef3rwb47kzyzjprhw6.png" alt="Using Web Sockets to overcome Chrome Tab Freezing" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Chrome 133, Google is taking a step forward in energy efficiency by &lt;a href="https://developer.chrome.com/blog/freezing-on-energy-saver?hl=en&amp;amp;ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;freezing inactive, CPU-intensive tabs when Energy Saver mode is active&lt;/a&gt;. While this is great for battery-conscious users, it can disrupt background processes like notifications, file uploads, or real-time updates. So, how do you adapt to these changes and ensure your app stays functional? One effective solution is to implement &lt;strong&gt;web sockets&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Web Sockets?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Web sockets provide a persistent, full-duplex communication channel between the client and server. Unlike traditional polling, which repeatedly sends HTTP requests to check for updates, web sockets maintain an open connection, enabling real-time communication with minimal overhead. This efficiency makes web sockets ideal for avoiding CPU-intensive tasks that could trigger Chrome's freezing mechanism.&lt;/p&gt;

&lt;p&gt;In other words, we would need to focus on how to handle &lt;strong&gt;Freezing with the Page Lifecycle API&lt;/strong&gt; (Use the &lt;code&gt;freeze&lt;/code&gt; and &lt;code&gt;resume&lt;/code&gt; events to pause non-essential processes and ensure the web socket remains functional), and &lt;strong&gt;Optimize Resource Usage&lt;/strong&gt; by minimizing the data sent over the socket to avoid being flagged as CPU-intensive. Compress messages if possible, and ensure the server handles idle connections efficiently. Some of the benefits of using web sockets include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Updates&lt;/strong&gt; : Maintain instant communication with users even when tabs are inactive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower Overhead&lt;/strong&gt; : Reduce CPU usage compared to polling, making your app less likely to be frozen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt; : Easily manage multiple connections for chat apps, notifications, or live feeds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For even greater efficiency, consider migrating background tasks to service workers. Service workers operate independently of the page and won’t be affected by freezing. Pairing web sockets with service workers ensures robust background functionality.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>websockets</category>
    </item>
    <item>
      <title>Simple 1-Minute Guide to add a Back-to-Top Button with Vue 3</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Wed, 18 Dec 2024 02:39:44 +0000</pubDate>
      <link>https://forem.com/dailysandbox/simple-1-minute-guide-to-add-a-back-to-top-button-with-vue-3-2j59</link>
      <guid>https://forem.com/dailysandbox/simple-1-minute-guide-to-add-a-back-to-top-button-with-vue-3-2j59</guid>
      <description>&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%2Fl5i1m9t7dxoo8t6hrtas.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%2Fl5i1m9t7dxoo8t6hrtas.gif" alt="Simple 1-Minute Guide to add a Back-to-Top Button with Vue 3" width="600" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding a "Back-to-Top" button to your Vue 3 app can significantly improve user experience, especially on long pages. This button remains hidden until the user scrolls down a specified distance, then smoothly scrolls them back to the top when clicked.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;What Makes It Great?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scroll Detection&lt;/strong&gt; : Monitors &lt;code&gt;window.scrollY&lt;/code&gt; to determine when the button should appear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth Scrolling&lt;/strong&gt; : Enhances navigation by animating the scroll action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern UI&lt;/strong&gt; : Sleek and minimalistic design that integrates seamlessly into any application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how to build it.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;The Code&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Script Setup&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The script uses Vue's Composition API to manage the button's visibility and handle the scroll behavior.&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;script setup&amp;gt;
const isVisible = ref(false); // Tracks the button's visibility

// Function to check scroll position and toggle visibility
const handleScroll = () =&amp;gt; {
    isVisible.value = window.scrollY &amp;gt; 200; // Show when scrolled &amp;gt; 200px
};

// Smoothly scroll to the top of the page
const scrollToTop = () =&amp;gt; {
    window.scrollTo({ top: 0, behavior: 'smooth' });
};

// Add and remove scroll event listeners
onMounted(() =&amp;gt; {
    window.addEventListener('scroll', handleScroll);
});

onBeforeUnmount(() =&amp;gt; {
    window.removeEventListener('scroll', handleScroll);
});
&amp;lt;/script&amp;gt;

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

&lt;/div&gt;






&lt;h4&gt;
  
  
  &lt;strong&gt;Template&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The button is conditionally displayed using &lt;code&gt;v-show&lt;/code&gt; and styled with an SVG icon to represent the back-to-top functionality.&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;template&amp;gt;
    &amp;lt;a v-show="isVisible" href="#" class="back-to-top-control" @click.prevent="scrollToTop"&amp;gt;
        &amp;lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"&amp;gt;
            &amp;lt;path stroke="none" d="M0 0h24v24H0z" fill="none" /&amp;gt;
            &amp;lt;path d="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-4.98 3.66l-.163 .01l-.086 .016l-.142 .045l-.113 .054l-.07 .043l-.095 .071l-.058 .054l-4 4l-.083 .094a1 1 0 0 0 1.497 1.32l2.293 -2.293v5.586l.007 .117a1 1 0 0 0 1.993 -.117v-5.585l2.293 2.292l.094 .083a1 1 0 0 0 1.32 -1.497l-4 -4l-.082 -.073l-.089 -.064l-.113 -.062l-.081 -.034l-.113 -.034l-.112 -.02l-.098 -.006z" stroke-width="0" fill="currentColor" /&amp;gt;
        &amp;lt;/svg&amp;gt;
    &amp;lt;/a&amp;gt;
&amp;lt;/template&amp;gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;v-show&lt;/code&gt;&lt;/strong&gt; : Ensures the button is rendered but hidden when not needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SVG Icon&lt;/strong&gt; : Enhances the button’s visual appeal.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;2228+ FREE&lt;/strong&gt; &lt;u&gt;&lt;b&gt;&lt;strong&gt;RESOURCES&lt;/strong&gt;&lt;/b&gt;&lt;/u&gt; &lt;strong&gt;FOR DEVELOPERS!! ❤️&lt;/strong&gt; 😍🥳 &lt;strong&gt;&lt;strong&gt;(updated daily)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1400+ Free HTML Templates&lt;br&gt;&lt;br&gt;
359+ Free News Articles&lt;br&gt;&lt;br&gt;
69+ Free AI Prompts&lt;br&gt;&lt;br&gt;
323+ Free Code Libraries&lt;br&gt;&lt;br&gt;
52+ Free Code Snippets &amp;amp; Boilerplates for Node, Nuxt, Vue, and more!&lt;br&gt;&lt;br&gt;
25+ Free Open Source Icon Libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Visit &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;dailysandbox.pro&lt;/a&gt; for free access to a treasure trove of resources!&lt;/p&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;Styles&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The button’s fixed position ensures it remains in view as the user scrolls. Hover effects provide a polished touch.&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;style lang="less" scoped&amp;gt;
.back-to-top-control {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1000;
    width: 60px;
    height: 60px;
    color: black;
    background-color: white;
    border-radius: 50%;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    opacity: 0.5;
    transition: opacity 0.3s ease;

    &amp;amp;:hover {
        opacity: 1;
    }

    &amp;gt; svg {
        width: 24px;
        height: 24px;
    }
}
&amp;lt;/style&amp;gt;

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;How It Works&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scroll Detection&lt;/strong&gt; : A scroll event listener checks if the user has scrolled more than 200 pixels. If true, &lt;code&gt;isVisible&lt;/code&gt; becomes &lt;code&gt;true&lt;/code&gt;, displaying the button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth Scroll&lt;/strong&gt; : Clicking the button triggers the &lt;code&gt;scrollToTop&lt;/code&gt; function, which uses &lt;code&gt;window.scrollTo&lt;/code&gt; with a &lt;code&gt;behavior&lt;/code&gt; of &lt;code&gt;smooth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle Hooks&lt;/strong&gt; : &lt;code&gt;onMounted&lt;/code&gt; and &lt;code&gt;onBeforeUnmount&lt;/code&gt; ensure that event listeners are properly managed to prevent memory leaks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more tips on web development, check out &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;&lt;strong&gt;DailySandbox&lt;/strong&gt;&lt;/a&gt; and sign up for our &lt;strong&gt;free newsletter&lt;/strong&gt; to stay ahead of the curve!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Add a Voice Search to your Nuxt3 App in 6 Easy Steps</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Mon, 09 Dec 2024 02:40:36 +0000</pubDate>
      <link>https://forem.com/dailysandbox/add-a-voice-search-to-your-nuxt3-app-in-6-easy-steps-23ob</link>
      <guid>https://forem.com/dailysandbox/add-a-voice-search-to-your-nuxt3-app-in-6-easy-steps-23ob</guid>
      <description>&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%2Fb608tjlw1a4vwszbcdbi.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%2Fb608tjlw1a4vwszbcdbi.png" alt="Add a Voice Search to your Nuxt3 App in 6 Easy Steps" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a world dominated by "Hey Siri" and "Okay Google," integrating voice search into your web application isn’t just an option—it’s a necessity. Imagine enabling your users to interact with your Nuxt 3 app hands-free, providing a seamless and futuristic experience. By leveraging the Web Speech API, we’ll transform your app into a voice-powered assistant that listens, understands, and reacts.&lt;/p&gt;




&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;First, let’s prepare your Nuxt 3 project. If you don’t already have one, it’s time to get started. Fire up your terminal and create a fresh Nuxt 3 app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nuxi init voice-search-app
cd voice-search-app
npm install
npm run dev

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

&lt;/div&gt;



&lt;p&gt;This will spin up the Nuxt development server. Open &lt;code&gt;http://localhost:3000&lt;/code&gt; in your browser, and you should see the Nuxt welcome page. With our environment ready, we’re set to introduce some voice-powered magic.&lt;/p&gt;




&lt;h3&gt;
  
  
  Building the Voice Search Component
&lt;/h3&gt;

&lt;p&gt;To begin, let’s create a dedicated component to handle voice recognition. Inside the &lt;code&gt;components&lt;/code&gt; directory, create a file called &lt;code&gt;VoiceSearch.vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch components/VoiceSearch.vue

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

&lt;/div&gt;



&lt;p&gt;This component will manage everything: starting and stopping the microphone, processing voice input, and displaying the transcript. Inside the file, define a reactive setup using Vue’s Composition API:&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;script setup&amp;gt;
import { ref, onMounted, onUnmounted } from 'vue';

const transcript = ref('');
const isListening = ref(false);
const isSupported = ref(false);

let recognition;

const startListening = () =&amp;gt; {
  if (!recognition) {
    console.error('SpeechRecognition instance is unavailable.');
    return;
  }
  isListening.value = true;
  recognition.start();
};

const stopListening = () =&amp;gt; {
  if (!recognition) {
    console.error('SpeechRecognition instance is unavailable.');
    return;
  }
  isListening.value = false;
  recognition.stop();
};

onMounted(() =&amp;gt; {
  const SpeechRecognition =
    window.SpeechRecognition || window.webkitSpeechRecognition;

  if (!SpeechRecognition) {
    console.warn('SpeechRecognition is not supported in this browser.');
    isSupported.value = false;
    return;
  }

  isSupported.value = true;
  recognition = new SpeechRecognition();
  recognition.continuous = true;
  recognition.interimResults = false;
  recognition.lang = 'en-US';

  recognition.onresult = (event) =&amp;gt; {
    const result = event.results[event.results.length - 1][0].transcript;
    transcript.value = result;
  };

  recognition.onerror = (event) =&amp;gt; {
    console.error('Recognition error:', event.error);
  };
});

onUnmounted(() =&amp;gt; {
  if (recognition) {
    recognition.abort();
  }
});
&amp;lt;/script&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;This setup initializes a SpeechRecognition instance, listens for results, and handles errors gracefully. The reactive variables &lt;code&gt;transcript&lt;/code&gt; and &lt;code&gt;isListening&lt;/code&gt; keep track of the user’s input and the system’s state.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2228+ FREE&lt;/strong&gt; &lt;u&gt;&lt;b&gt;&lt;strong&gt;RESOURCES&lt;/strong&gt;&lt;/b&gt;&lt;/u&gt; &lt;strong&gt;FOR DEVELOPERS!! ❤️&lt;/strong&gt; 😍🥳 &lt;strong&gt;&lt;strong&gt;(updated daily)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1400+ Free HTML Templates&lt;br&gt;&lt;br&gt;
359+ Free News Articles&lt;br&gt;&lt;br&gt;
69+ Free AI Prompts&lt;br&gt;&lt;br&gt;
323+ Free Code Libraries&lt;br&gt;&lt;br&gt;
52+ Free Code Snippets &amp;amp; Boilerplates for Node, Nuxt, Vue, and more!&lt;br&gt;&lt;br&gt;
25+ Free Open Source Icon Libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Visit &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;dailysandbox.pro&lt;/a&gt; for free access to a treasure trove of resources!&lt;/p&gt;




&lt;h3&gt;
  
  
  Designing the User Interface
&lt;/h3&gt;

&lt;p&gt;With the logic in place, it’s time to craft the interface. The component template includes buttons to start and stop listening, as well as a transcript display:&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;template&amp;gt;
  &amp;lt;div class="voice-search"&amp;gt;
    &amp;lt;button
      @click="startListening"
      :disabled="isListening"
      class="start-button"&amp;gt;
      🎙️ Start Voice Search
    &amp;lt;/button&amp;gt;
    &amp;lt;button
      @click="stopListening"
      :disabled="!isListening"
      class="stop-button"&amp;gt;
      🛑 Stop
    &amp;lt;/button&amp;gt;
    &amp;lt;p v-if="isSupported"&amp;gt;
      &amp;lt;strong&amp;gt;Transcript:&amp;lt;/strong&amp;gt; {{ transcript || 'Say something...' }}
    &amp;lt;/p&amp;gt;
    &amp;lt;p v-else&amp;gt;Your browser does not support voice search.&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Add some simple styles to ensure a clean and user-friendly layout:&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;style scoped&amp;gt;
.voice-search {
  text-align: center;
  padding: 20px;
  font-family: Arial, sans-serif;
}

button {
  padding: 10px 20px;
  margin: 5px;
  border: none;
  border-radius: 5px;
  color: white;
  font-size: 16px;
  cursor: pointer;
}

.start-button {
  background-color: #4caf50;
}

.start-button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

.stop-button {
  background-color: #f44336;
}

.stop-button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

p {
  margin-top: 20px;
  font-size: 18px;
  color: #333;
}
&amp;lt;/style&amp;gt;

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Bringing It All Together in Nuxt
&lt;/h3&gt;

&lt;p&gt;To use the voice search component, import it into your app’s main page. Open &lt;code&gt;pages/index.vue&lt;/code&gt; and replace its contents with:&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;template&amp;gt;
  &amp;lt;div class="app"&amp;gt;
    &amp;lt;h1&amp;gt;Nuxt 3 Voice Search&amp;lt;/h1&amp;gt;
    &amp;lt;VoiceSearch /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;
import VoiceSearch from '~/components/VoiceSearch.vue';
&amp;lt;/script&amp;gt;

&amp;lt;style scoped&amp;gt;
.app {
  display: grid;
  place-items: center;
  height: 100vh;
  text-align: center;
}
&amp;lt;/style&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Start your app with &lt;code&gt;npm run dev&lt;/code&gt;, and visit &lt;code&gt;http://localhost:3000&lt;/code&gt; to see the magic unfold. Click "Start Voice Search," speak into your microphone, and watch as your words appear on the screen in real time.&lt;/p&gt;




&lt;h3&gt;
  
  
  Enhancing the Experience
&lt;/h3&gt;

&lt;p&gt;Voice search is already impressive, but you can make it even better:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle Fallbacks for Unsupported Browsers&lt;/strong&gt; : Ensure users can still interact with the app even if their browser doesn’t support the Web Speech API:&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;p v-else&amp;gt;Your browser does not support voice search. Please type your query manually.&amp;lt;/p&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Link the Transcript to a Search&lt;/strong&gt; : Add a button to perform a search based on the transcript:&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;button @click="handleSearch" class="search-button"&amp;gt;🔍 Search&amp;lt;/button&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;In the script setup, define the search function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleSearch = () =&amp;gt; {
  console.log('Searching for:', transcript.value);
};

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

&lt;/div&gt;






&lt;p&gt;With just a few lines of code, you’ve transformed your Nuxt 3 app into a cutting-edge tool that listens to users’ voices. Voice search isn’t just a trendy feature—it’s a testament to the power of modern web APIs and their ability to make technology more accessible and interactive.&lt;/p&gt;

&lt;p&gt;By embracing tools like the Web Speech API, you’re not just building apps; you’re shaping the future of user interactions. So go ahead, deploy your app, and let your users experience the magic of voice search.&lt;/p&gt;

&lt;p&gt;For more tips on web development, check out &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;&lt;strong&gt;DailySandbox&lt;/strong&gt;&lt;/a&gt; and sign up for our &lt;strong&gt;free newsletter&lt;/strong&gt; to stay ahead of the curve!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>nuxt</category>
      <category>javascript</category>
      <category>vue</category>
    </item>
    <item>
      <title>How to Copy Text to Clipboard</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Fri, 29 Nov 2024 01:37:50 +0000</pubDate>
      <link>https://forem.com/dailysandbox/how-to-copy-text-to-clipboard-4kef</link>
      <guid>https://forem.com/dailysandbox/how-to-copy-text-to-clipboard-4kef</guid>
      <description>&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%2Fzhkakholiizk4mkpqz97.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%2Fzhkakholiizk4mkpqz97.gif" alt="How to Copy Text to Clipboard" width="600" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copying text to the clipboard is a common requirement in modern web applications. While many vanilla JavaScript solutions exist, today we’ll use &lt;strong&gt;Clipboard.js&lt;/strong&gt; , a lightweight library, and &lt;strong&gt;Hint.css&lt;/strong&gt; , a simple tooltip library, to create an elegant copy-to-clipboard feature.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: HTML Structure
&lt;/h3&gt;

&lt;p&gt;The HTML remains the same. It includes a link styled with Hint.css to show a tooltip when hovering over the copy icon.&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;a href="#" class="app-clipboard-link hint hint-bottom" data-hint="Copy link to clipboard"&amp;gt;
    &amp;lt;i class="ti ti-copy"&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/a&amp;gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;class="hint hint-bottom"&lt;/code&gt;&lt;/strong&gt; : Adds a bottom-aligned tooltip using Hint.css.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;data-hint="Copy link to clipboard"&lt;/code&gt;&lt;/strong&gt; : The tooltip text.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Step 2: JavaScript
&lt;/h3&gt;

&lt;p&gt;Here’s the updated code in plain JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Select the clipboard control
const control = document.querySelector('.app-clipboard-link');

// Set the clipboard text
control.setAttribute('data-clipboard-text', 'https://google.com');

// Initialize Clipboard.js
const clipboard = new ClipboardJS(control, {
    text: (trigger) =&amp;gt; trigger.getAttribute('data-clipboard-text'),
});

// Handle the success event
clipboard.on('success', (event) =&amp;gt; {
    // Update the tooltip to show "Copied"
    control.setAttribute('data-hint', 'Copied');

    // Reset the tooltip after 3 seconds
    setTimeout(() =&amp;gt; {
        control.setAttribute('data-hint', 'Copy link to clipboard');
    }, 3000);
});

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

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;2254+ FREE&lt;/strong&gt; &lt;u&gt;&lt;b&gt;&lt;strong&gt;RESOURCES&lt;/strong&gt;&lt;/b&gt;&lt;/u&gt; &lt;strong&gt;FOR DEVELOPERS!! ❤️&lt;/strong&gt; 😍🥳 &lt;strong&gt;&lt;strong&gt;(updated daily)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1400+ Free HTML Templates&lt;br&gt;&lt;br&gt;
372+ Free News Articles&lt;br&gt;&lt;br&gt;
72+ Free AI Prompts&lt;br&gt;&lt;br&gt;
333+ Free Code Libraries&lt;br&gt;&lt;br&gt;
52+ Free Code Snippets &amp;amp; Boilerplates for Node, Nuxt, Vue, and more!&lt;br&gt;&lt;br&gt;
25+ Free Open Source Icon Libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Visit &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;dailysandbox.pro&lt;/a&gt; for free access to a treasure trove of resources!&lt;/p&gt;




&lt;h3&gt;
  
  
  Explanation of the Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This sets the &lt;code&gt;data-clipboard-text&lt;/code&gt; attribute dynamically to the desired URL.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;text&lt;/code&gt;: Defines a callback function that returns the value of the &lt;code&gt;data-clipboard-text&lt;/code&gt; attribute for the clicked element.&lt;/li&gt;
&lt;li&gt;Updates the tooltip to "Copied" when the clipboard action is successful.&lt;/li&gt;
&lt;li&gt;Resets the tooltip text back to "Copy link to clipboard" after 3 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Handling the Success Event&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clipboard.on('success', (event) =&amp;gt; {
    control.setAttribute('data-hint', 'Copied');
    setTimeout(() =&amp;gt; {
        control.setAttribute('data-hint', 'Copy link to clipboard');
    }, 3000);
});

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Initializing Clipboard.js&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const clipboard = new ClipboardJS(control, {
    text: (trigger) =&amp;gt; trigger.getAttribute('data-clipboard-text'),
});

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Setting the Clipboard Text&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;control.setAttribute('data-clipboard-text', 'https://google.com');

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Advantages of This Approach
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No jQuery Dependency&lt;/strong&gt; : By using plain JavaScript, the solution is lightweight and compatible with modern web standards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hint.css Integration&lt;/strong&gt; : Provides a clean, visually appealing tooltip without additional JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clipboard.js Simplicity&lt;/strong&gt; : Handles clipboard interactions efficiently without requiring custom logic.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;With just a few lines of plain JavaScript and the power of Clipboard.js and Hint.css, you’ve implemented a clean, user-friendly copy-to-clipboard feature. This approach avoids unnecessary dependencies while maintaining functionality and elegance.&lt;/p&gt;

&lt;p&gt;Now, go ahead and try it out! Your users will appreciate the simplicity and responsiveness of this feature. 🚀&lt;/p&gt;

&lt;p&gt;For more tips on web development, check out &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;&lt;strong&gt;DailySandbox&lt;/strong&gt;&lt;/a&gt; and sign up for our &lt;strong&gt;free newsletter&lt;/strong&gt; to stay ahead of the curve!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>Building a 6-Digit Passcode Input in Plain JavaScript</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Thu, 28 Nov 2024 04:42:46 +0000</pubDate>
      <link>https://forem.com/dailysandbox/building-a-6-digit-passcode-input-in-plain-javascript-1a45</link>
      <guid>https://forem.com/dailysandbox/building-a-6-digit-passcode-input-in-plain-javascript-1a45</guid>
      <description>&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%2F3bdctdfrclbrjrq3mdkl.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%2F3bdctdfrclbrjrq3mdkl.gif" alt="Building a 6-Digit Passcode Input in Plain JavaScript" width="600" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll create a seamless passcode input system for a 6-digit activation code. This setup ensures user-friendliness, supports direct typing or pasting of the code, and triggers a verification function when all digits are entered.&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 1: HTML Structure
&lt;/h3&gt;

&lt;p&gt;Start with a form that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A placeholder for the passcode input fields.&lt;/li&gt;
&lt;li&gt;A link for resending the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the updated HTML:&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;form class="app-signup-wrapper"&amp;gt;
    &amp;lt;div class="app-plan-wrapper"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;label class="form-label app-activation-label"&amp;gt;
        &amp;lt;p&amp;gt;We have sent a 6-number code to&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;user@example.com&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;The code expires in 1 hour, so please hurry.&amp;lt;/p&amp;gt;
    &amp;lt;/label&amp;gt;
    &amp;lt;div class="app-code-activation-wrapper"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;a href="#" class="app-code-activation-resend" data-sent="false"&amp;gt;Did not receive it? Re-Send Now&amp;lt;/a&amp;gt;
&amp;lt;/form&amp;gt;

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Part 2: JavaScript for Passcode Inputs
&lt;/h3&gt;

&lt;p&gt;We’ll create six input fields dynamically, handle user interactions, and trigger the verification logic upon completion.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Dynamically Add Input Fields
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const codeActivationWrapper = document.querySelector('.app-code-activation-wrapper');
const numOfFields = 6;

// Create and append six input fields
for (let i = 0; i &amp;lt; numOfFields; i++) {
    const input = document.createElement('input');
    input.type = 'text';
    input.className = 'form-control app-activation-code-input';
    input.maxLength = 1;
    codeActivationWrapper.appendChild(input);
}

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

&lt;/div&gt;



&lt;p&gt;This script adds six input fields to the &lt;code&gt;.app-code-activation-wrapper&lt;/code&gt; div, each restricted to one character using &lt;code&gt;maxLength&lt;/code&gt;.&lt;/p&gt;




&lt;h4&gt;
  
  
  Step 2: Handling Paste Events
&lt;/h4&gt;

&lt;p&gt;Allow users to paste the full code. If the pasted text is valid, split it across the input fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fields = document.querySelectorAll('.app-activation-code-input');

fields.forEach((field, index) =&amp;gt; {
    field.addEventListener('paste', (event) =&amp;gt; {
        event.preventDefault();
        const pasteData = (event.clipboardData || window.clipboardData).getData('text').trim();

        if (/^\d{6}$/.test(pasteData)) {
            // Distribute pasted digits into input fields
            [...pasteData].forEach((char, idx) =&amp;gt; {
                if (fields[idx]) fields[idx].value = char;
            });
            fields[5].focus(); // Focus the last field
            verifyActivationCode(); // Call verification
        }
    });
});

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

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;2228+ FREE&lt;/strong&gt; &lt;u&gt;&lt;b&gt;&lt;strong&gt;RESOURCES&lt;/strong&gt;&lt;/b&gt;&lt;/u&gt; &lt;strong&gt;FOR DEVELOPERS!! ❤️&lt;/strong&gt; 😍🥳 &lt;strong&gt;&lt;strong&gt;(updated daily)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1400+ Free HTML Templates&lt;br&gt;&lt;br&gt;
359+ Free News Articles&lt;br&gt;&lt;br&gt;
69+ Free AI Prompts&lt;br&gt;&lt;br&gt;
323+ Free Code Libraries&lt;br&gt;&lt;br&gt;
52+ Free Code Snippets &amp;amp; Boilerplates for Node, Nuxt, Vue, and more!&lt;br&gt;&lt;br&gt;
25+ Free Open Source Icon Libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Visit &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;dailysandbox.pro&lt;/a&gt; for free access to a treasure trove of resources!&lt;/p&gt;




&lt;h4&gt;
  
  
  Step 3: Handling Input and Navigation
&lt;/h4&gt;

&lt;p&gt;Automatically move to the next field on input and handle backspace for navigation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields.forEach((field, index) =&amp;gt; {
    field.addEventListener('input', () =&amp;gt; {
        if (field.value &amp;amp;&amp;amp; index &amp;lt; fields.length - 1) {
            fields[index + 1].focus();
        }
        checkCompletion(); // Check if all fields are filled
    });

    field.addEventListener('keydown', (event) =&amp;gt; {
        if (event.key === 'Backspace' &amp;amp;&amp;amp; !field.value &amp;amp;&amp;amp; index &amp;gt; 0) {
            fields[index - 1].focus();
        }
    });
});

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

&lt;/div&gt;






&lt;h4&gt;
  
  
  Step 4: Check for Completion
&lt;/h4&gt;

&lt;p&gt;When all six fields are filled, concatenate their values and trigger the &lt;code&gt;verifyActivationCode&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const checkCompletion = () =&amp;gt; {
    const activationCode = Array.from(fields).map((field) =&amp;gt; field.value).join('');
    if (activationCode.length === 6) {
        verifyActivationCode(activationCode);
    }
};

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

&lt;/div&gt;






&lt;h4&gt;
  
  
  Step 5: Verification Logic
&lt;/h4&gt;

&lt;p&gt;Simulate the verification process with a function that sends the code to a backend server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const verifyActivationCode = (activationCode) =&amp;gt; {
    console.log('Verifying code:', activationCode);

    const spinner = document.createElement('div');
    spinner.className = 'spinner';
    document.body.appendChild(spinner);

    // Simulate an AJAX request
    setTimeout(() =&amp;gt; {
        spinner.remove();
        if (activationCode === '123456') {
            alert('Code verified successfully!');
        } else {
            alert('Invalid code. Try again.');
            fields.forEach((field) =&amp;gt; (field.value = ''));
            fields[0].focus();
        }
    }, 2000);
};

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Part 3: Handling the Resend Link
&lt;/h3&gt;

&lt;p&gt;Add functionality to resend the activation 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 resendLink = document.querySelector('.app-code-activation-resend');

resendLink.addEventListener('click', (event) =&amp;gt; {
    event.preventDefault();

    // Simulate a resend action
    resendLink.setAttribute('data-sent', 'true');
    setTimeout(() =&amp;gt; {
        resendLink.setAttribute('data-sent', 'false');
        alert('Activation code resent!');
    }, 5000);
});

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Complete JavaScript Code
&lt;/h3&gt;

&lt;p&gt;Here’s the complete script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const codeActivationWrapper = document.querySelector('.app-code-activation-wrapper');
const resendLink = document.querySelector('.app-code-activation-resend');
const numOfFields = 6;

// Create input fields dynamically
for (let i = 0; i &amp;lt; numOfFields; i++) {
    const input = document.createElement('input');
    input.type = 'text';
    input.className = 'form-control app-activation-code-input';
    input.maxLength = 1;
    codeActivationWrapper.appendChild(input);
}

const fields = document.querySelectorAll('.app-activation-code-input');

// Handle paste events
fields.forEach((field, index) =&amp;gt; {
    field.addEventListener('paste', (event) =&amp;gt; {
        event.preventDefault();
        const pasteData = (event.clipboardData || window.clipboardData).getData('text').trim();

        if (/^\d{6}$/.test(pasteData)) {
            [...pasteData].forEach((char, idx) =&amp;gt; {
                if (fields[idx]) fields[idx].value = char;
            });
            fields[5].focus();
            verifyActivationCode(Array.from(fields).map((field) =&amp;gt; field.value).join(''));
        }
    });
});

// Handle input and navigation
fields.forEach((field, index) =&amp;gt; {
    field.addEventListener('input', () =&amp;gt; {
        if (field.value &amp;amp;&amp;amp; index &amp;lt; fields.length - 1) {
            fields[index + 1].focus();
        }
        checkCompletion();
    });

    field.addEventListener('keydown', (event) =&amp;gt; {
        if (event.key === 'Backspace' &amp;amp;&amp;amp; !field.value &amp;amp;&amp;amp; index &amp;gt; 0) {
            fields[index - 1].focus();
        }
    });
});

// Check for completion
const checkCompletion = () =&amp;gt; {
    const activationCode = Array.from(fields).map((field) =&amp;gt; field.value).join('');
    if (activationCode.length === 6) {
        verifyActivationCode(activationCode);
    }
};

// Simulate code verification
const verifyActivationCode = (activationCode) =&amp;gt; {
    console.log('Verifying code:', activationCode);

    const spinner = document.createElement('div');
    spinner.className = 'spinner';
    document.body.appendChild(spinner);

    setTimeout(() =&amp;gt; {
        spinner.remove();
        if (activationCode === '123456') {
            alert('Code verified successfully!');
        } else {
            alert('Invalid code. Try again.');
            fields.forEach((field) =&amp;gt; (field.value = ''));
            fields[0].focus();
        }
    }, 2000);
};

// Handle resend link
resendLink.addEventListener('click', (event) =&amp;gt; {
    event.preventDefault();

    resendLink.setAttribute('data-sent', 'true');
    setTimeout(() =&amp;gt; {
        resendLink.setAttribute('data-sent', 'false');
        alert('Activation code resent!');
    }, 5000);
});

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This solution creates a user-friendly, fully functional 6-digit passcode system using &lt;strong&gt;plain JavaScript&lt;/strong&gt;. It supports pasting, input validation, seamless navigation, and code verification, ensuring a smooth experience. Enhance it further by connecting the verification logic to your backend or adding animations for better visual feedback.&lt;/p&gt;

&lt;p&gt;For more tips on web development, check out &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;&lt;strong&gt;DailySandbox&lt;/strong&gt;&lt;/a&gt; and sign up for our &lt;strong&gt;free newsletter&lt;/strong&gt; to stay ahead of the curve!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Adding a "Shaking" Effect to Login Popovers for a Visual Error Cue</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Wed, 27 Nov 2024 01:51:13 +0000</pubDate>
      <link>https://forem.com/dailysandbox/adding-a-shaking-effect-to-login-popovers-for-a-visual-error-cue-3ggb</link>
      <guid>https://forem.com/dailysandbox/adding-a-shaking-effect-to-login-popovers-for-a-visual-error-cue-3ggb</guid>
      <description>&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%2Fytvlxzvivozin9w2k37h.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%2Fytvlxzvivozin9w2k37h.gif" alt="Adding a " width="600" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, subtle design elements can make a huge difference in user experience. Instead of displaying traditional error messages, a "shaking" effect on a login popover provides a clear and immediate indication that something went wrong. This tutorial will guide you through implementing this functionality using &lt;strong&gt;vanilla JavaScript&lt;/strong&gt; , &lt;strong&gt;CSS animations&lt;/strong&gt; , and the open-source library &lt;strong&gt;Tippy.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Our objective is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a login popover with Tippy.js.&lt;/li&gt;
&lt;li&gt;Add the "shaking" effect when errors occur.&lt;/li&gt;
&lt;li&gt;Automatically reset the animation once the shake ends.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 1: Setting Up the Shaking Animation with CSS
&lt;/h3&gt;

&lt;p&gt;We’ll start by defining a reusable CSS animation for the shake effect. The &lt;code&gt;@keyframes&lt;/code&gt; rule below mimics a side-to-side shake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@keyframes shaking {
    10%, 90% { transform: translate3d(-1px, 0, 0); }
    20%, 80% { transform: translate3d(2px, 0, 0); }
    30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
    40%, 60% { transform: translate3d(4px, 0, 0); }
}
.shake {
    animation: shaking 0.82s cubic-bezier(.36,.07,.19,.97) both;
}

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyframe Details&lt;/strong&gt; : The &lt;code&gt;translate3d&lt;/code&gt; property moves the popover slightly to the left and right to create the shaking effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Class&lt;/strong&gt; : Applying the &lt;code&gt;shake&lt;/code&gt; class to any element triggers the animation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Part 2: Creating the Login Popover with Tippy.js
&lt;/h3&gt;

&lt;p&gt;We’ll use Tippy.js to create a clickable login popover. The popover will include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An email input field.&lt;/li&gt;
&lt;li&gt;A "Sign In" button.&lt;/li&gt;
&lt;li&gt;Event listeners to handle sign-in logic and the shake effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the core JavaScript for setting up the popover:&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: The Sign-In Class
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP.Signin = class {
    constructor($target) {
        this.values = {}; // Store any required state
        if ($target) this.$target = $target; // The DOM element triggering the popover
        this.init(); // Initialize the popover
        return this;
    }

    // Trigger the shake animation
    shake() {
        this.$tippy.classList.add('shake');
        return this;
    }

    // Handle Sign-In button clicks
    onSigninClicked(event) {
        event.preventDefault();

        // Retrieve the entered email
        let email = document.querySelector('.app-signin-email').value;

        // Error and success handlers
        let _onError = () =&amp;gt; this.shake();
        let _onSuccess = (response) =&amp;gt; {
            if (response.errors.length) {
                this.shake(); // Shake on error
            } else {
                // Handle successful login
                console.log('Login successful!');
            }
        };

        // Simulate a backend login request
        let form_data = new FormData();
        form_data.append('method', 'quickSignIn');
        form_data.append('email', email);

        axios({
            method: 'POST',
            url: 'path/to/server',
            data: form_data,
            headers: { 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' },
        })
            .then((response) =&amp;gt; (typeof response.data === 'string' ? JSON.parse(response.data) : response.data))
            .then(_onSuccess)
            .catch(_onError);
    }

    // Define the HTML content of the popover
    getContent() {
        return `
            &amp;lt;div class="app-signin"&amp;gt;
                &amp;lt;label class="form-label app-signin-form-elm app-signin-label"&amp;gt;Account Email&amp;lt;/label&amp;gt;
                &amp;lt;input type="text" class="app-signin-form-elm app-signin-input app-signin-email" value="" placeholder="e.g., jdoe@email.com"&amp;gt;
                &amp;lt;a href="#" class="app-signin-form-elm app-signin-btn"&amp;gt;Sign In&amp;lt;/a&amp;gt;                                      
            &amp;lt;/div&amp;gt;
        `;
    }

    // Initialize the popover
    init() {
        let $content = this.getContent();

        if (this.$target) {
            tippy(this.$target, {
                content: $content,
                allowHTML: true,
                trigger: 'click',
                hideOnClick: true,
                interactive: true,
                arrow: true,
                animation: 'scale',
                placement: 'bottom',
                theme: 'white',
                offset: [0, 15],
                onMount: () =&amp;gt; {
                    this.$tippy = document.querySelector('.tippy-box'); // The popover box
                    this.$content = document.querySelector('.app-signin');
                    this.$signin_btn = document.querySelector('.app-signin-btn');
                    this.$email = document.querySelector('.app-signin-email');

                    // Add event listeners
                    this.$signin_btn.addEventListener('click', this.onSigninClicked.bind(this));
                    this.$email.addEventListener('keypress', (event) =&amp;gt; {
                        if (event.key === 'Enter') {
                            event.preventDefault();
                            this.$signin_btn.click();
                        }
                    });

                    // Remove the shake class after animation ends
                    this.$tippy.addEventListener('animationend', () =&amp;gt; {
                        this.$tippy.classList.remove('shake');
                    });

                    this.$email.focus(); // Auto-focus on the email input
                },
            });
        }
    }
};

// Initialize the Sign-In popover on page load
document.addEventListener('DOMContentLoaded', () =&amp;gt; {
    new APP.Signin(document.querySelector('.app-header-control-signin'));
});

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

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;2228+ FREE&lt;/strong&gt; &lt;u&gt;&lt;b&gt;&lt;strong&gt;RESOURCES&lt;/strong&gt;&lt;/b&gt;&lt;/u&gt; &lt;strong&gt;FOR DEVELOPERS!! ❤️&lt;/strong&gt; 😍🥳 &lt;strong&gt;&lt;strong&gt;(updated daily)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1400+ Free HTML Templates&lt;br&gt;&lt;br&gt;
359+ Free News Articles&lt;br&gt;&lt;br&gt;
69+ Free AI Prompts&lt;br&gt;&lt;br&gt;
323+ Free Code Libraries&lt;br&gt;&lt;br&gt;
52+ Free Code Snippets &amp;amp; Boilerplates for Node, Nuxt, Vue, and more!&lt;br&gt;&lt;br&gt;
25+ Free Open Source Icon Libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Visit &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;dailysandbox.pro&lt;/a&gt; for free access to a treasure trove of resources!&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 3: Wiring It All Together
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Simulated Backend&lt;/strong&gt; : If you don’t have a real server, mock responses with a promise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mockServer = (email) =&amp;gt;
    new Promise((resolve, reject) =&amp;gt; {
        setTimeout(() =&amp;gt; {
            if (email === 'test@email.com') resolve({ errors: [] });
            else reject({ errors: ['Invalid email'] });
        }, 500);
    });

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CSS for the Popover&lt;/strong&gt; : Ensure the popover matches your design. Here’s a simple setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.app-signin {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.app-signin-email {
    width: 100%;
    padding: 10px;
    font-size: 1rem;
    border: 1px solid #ccc;
    border-radius: 4px;
}

.app-signin-btn {
    background-color: #007bff;
    color: white;
    text-align: center;
    padding: 10px;
    border-radius: 4px;
    cursor: pointer;
}

.app-signin-btn:hover {
    background-color: #0056b3;
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HTML Trigger Element&lt;/strong&gt; : Add a trigger element to your HTML:&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;button class="app-header-control-signin"&amp;gt;Sign In&amp;lt;/button&amp;gt;

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Part 4: Test the Shaking Effect
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Sign In&lt;/strong&gt; button to open the popover.&lt;/li&gt;
&lt;li&gt;Enter an invalid email and submit. Watch the popover shake, indicating an error.&lt;/li&gt;
&lt;li&gt;Enter a valid email and see no shaking—success!&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Conclusion: A User-Friendly Error Cue
&lt;/h3&gt;

&lt;p&gt;The shaking effect enhances user experience by offering a clear, intuitive error indicator without cluttering the UI with extra messages. Combined with Tippy.js for a sleek popover and vanilla JS for interactivity, this setup is clean, functional, and visually appealing.&lt;/p&gt;

&lt;p&gt;Keep experimenting and tweaking—because great UX is all about the details!&lt;/p&gt;

&lt;p&gt;For more tips on web development, check out &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;&lt;strong&gt;DailySandbox&lt;/strong&gt;&lt;/a&gt; and sign up for our &lt;strong&gt;free newsletter&lt;/strong&gt; to stay ahead of the curve!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>Part 2 - Enhancing Repository Descriptions with OpenAI for Maximum Impact</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Tue, 26 Nov 2024 03:07:57 +0000</pubDate>
      <link>https://forem.com/dailysandbox/part-2-enhancing-repository-descriptions-with-openai-for-maximum-impact-4aao</link>
      <guid>https://forem.com/dailysandbox/part-2-enhancing-repository-descriptions-with-openai-for-maximum-impact-4aao</guid>
      <description>&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%2Fj98vw2bvuicregd24b16.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%2Fj98vw2bvuicregd24b16.png" alt="Part 2 - Enhancing Repository Descriptions with OpenAI for Maximum Impact" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you missed it, here is &lt;a href="https://blog.dailysandbox.pro/building-a-web-crawler-in-node-js-to-discover-ai-powered-javascript-repos-on-github/" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, you’ve built a GitHub crawler that fetches JavaScript repositories working with AI. Impressive! But what if we could go one step further and give those descriptions a sensational makeover? Imagine turning a bland description like “A library for machine learning” into something electrifying like “Revolutionize your AI projects with this groundbreaking machine learning library!”&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll integrate OpenAI into our Node.js script to rewrite repository descriptions, making them more captivating and attention-grabbing. Let’s dive in.&lt;/p&gt;

&lt;p&gt;Let's not forget to install the &lt;code&gt;dotenv&lt;/code&gt; package, if not already&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install dotenv

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Part 1: Enhancing the Crawler
&lt;/h3&gt;

&lt;p&gt;Update your &lt;code&gt;crawler.js&lt;/code&gt; to include OpenAI.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Import OpenAI and Configure the API
&lt;/h4&gt;

&lt;p&gt;At the top of your file, add the necessary imports and set up OpenAI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require('dotenv').config(); // Load environment variables
const { Configuration, OpenAIApi } = require('openai');
const axios = require('axios');
const cheerio = require('cheerio');

// Configure OpenAI
const configuration = new Configuration({
    apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

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

&lt;/div&gt;






&lt;h4&gt;
  
  
  Step 2: Add a Function to Rewrite Descriptions
&lt;/h4&gt;

&lt;p&gt;Create a function that uses OpenAI to rewrite descriptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const rewriteDescription = async (description) =&amp;gt; {
    try {
        const response = await openai.createCompletion({
            model: 'text-davinci-003',
            prompt: `Rewrite the following repository description to make it more sensational and engaging:\n"${description}"`,
            max_tokens: 100,
            temperature: 0.7,
        });

        return response.data.choices[0].text.trim();
    } catch (error) {
        console.error('Error rewriting description:', error.message);
        return description; // Return the original description on error
    }
};

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

&lt;/div&gt;



&lt;p&gt;This function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sends the original description to OpenAI with a prompt to rewrite it.&lt;/li&gt;
&lt;li&gt;Returns the enhanced version or the original if an error occurs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h4&gt;
  
  
  Step 3: Integrate the Rewriter into the Crawler
&lt;/h4&gt;

&lt;p&gt;Update your &lt;code&gt;fetchRepositories&lt;/code&gt; function to enhance descriptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fetchRepositories = async () =&amp;gt; {
    try {
        const { data } = await axios.get(SEARCH_URL);
        const $ = cheerio.load(data);

        const repositories = [];
        for (const element of $('.repo-list-item').toArray()) {
            const repoName = $(element).find('a').text().trim();
            const repoUrl = `https://github.com${$(element).find('a').attr('href')}`;
            const repoDescription = $(element).find('.mb-1').text().trim();

            const enhancedDescription = await rewriteDescription(repoDescription);

            repositories.push({
                name: repoName,
                url: repoUrl,
                description: enhancedDescription,
            });
        }

        return repositories;
    } catch (error) {
        console.error('Error fetching repositories:', error.message);
        return [];
    }
};

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

&lt;/div&gt;



&lt;p&gt;Here’s the key change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each repository, we call &lt;code&gt;rewriteDescription&lt;/code&gt; to enhance its description before adding it to the results.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;2228+ FREE&lt;/strong&gt; &lt;u&gt;&lt;b&gt;&lt;strong&gt;RESOURCES&lt;/strong&gt;&lt;/b&gt;&lt;/u&gt; &lt;strong&gt;FOR DEVELOPERS!! ❤️&lt;/strong&gt; 😍🥳 &lt;strong&gt;&lt;strong&gt;(updated daily)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1400+ Free HTML Templates&lt;br&gt;&lt;br&gt;
359+ Free News Articles&lt;br&gt;&lt;br&gt;
69+ Free AI Prompts&lt;br&gt;&lt;br&gt;
323+ Free Code Libraries&lt;br&gt;&lt;br&gt;
52+ Free Code Snippets &amp;amp; Boilerplates for Node, Nuxt, Vue, and more!&lt;br&gt;&lt;br&gt;
25+ Free Open Source Icon Libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Visit &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;dailysandbox.pro&lt;/a&gt; for free access to a treasure trove of resources!&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 2: Displaying the Results
&lt;/h3&gt;

&lt;p&gt;Finally, log the enhanced repositories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(async () =&amp;gt; {
    const repositories = await fetchRepositories();
    console.log('Enhanced AI-Powered JavaScript Repositories:', repositories);
})();

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Part 3: Running the Script
&lt;/h3&gt;

&lt;p&gt;Run your updated crawler script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node crawler.js

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

&lt;/div&gt;



&lt;p&gt;You’ll see a list of AI-related JavaScript repositories with their descriptions transformed into sensational, engaging text.&lt;/p&gt;




&lt;h3&gt;
  
  
  Example Output
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "ai-library",
    "url": "https://github.com/user/ai-library",
    "description": "A library for AI models in JavaScript."
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "ai-library",
    "url": "https://github.com/user/ai-library",
    "description": "Unlock the full potential of JavaScript with this cutting-edge AI library—designed to empower developers with next-gen models!"
}

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Part 4: Enhancing and Scaling
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Batch Processing&lt;/strong&gt; : If GitHub returns a large number of repositories, implement batching to avoid exceeding OpenAI’s API limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customization&lt;/strong&gt; : Adjust the prompt or OpenAI parameters (&lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;max_tokens&lt;/code&gt;) to suit your desired tone and creativity level.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Save Results&lt;/strong&gt; : Save the enhanced repositories to a JSON file for easy reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');

const saveToFile = (data) =&amp;gt; {
    fs.writeFileSync('enhanced_repositories.json', JSON.stringify(data, null, 2));
    console.log('Enhanced data saved to enhanced_repositories.json');
};

// Save data after fetching
(async () =&amp;gt; {
    const repositories = await fetchRepositories();
    saveToFile(repositories);
})();

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

&lt;/div&gt;






&lt;p&gt;For more tips on web development, check out &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;&lt;strong&gt;DailySandbox&lt;/strong&gt;&lt;/a&gt; and sign up for our &lt;strong&gt;free newsletter&lt;/strong&gt; to stay ahead of the curve!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>ai</category>
      <category>node</category>
    </item>
    <item>
      <title>Building a Web Crawler in Node.js to Discover AI-Powered JavaScript Repos on GitHub</title>
      <dc:creator>Art</dc:creator>
      <pubDate>Mon, 25 Nov 2024 03:33:16 +0000</pubDate>
      <link>https://forem.com/dailysandbox/building-a-web-crawler-in-nodejs-to-discover-ai-powered-javascript-repos-on-github-49mm</link>
      <guid>https://forem.com/dailysandbox/building-a-web-crawler-in-nodejs-to-discover-ai-powered-javascript-repos-on-github-49mm</guid>
      <description>&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%2Foeg73j19m5d95t1b6s14.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%2Foeg73j19m5d95t1b6s14.png" alt="Building a Web Crawler in Node.js to Discover AI-Powered JavaScript Repos on GitHub" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub is a treasure trove of innovative projects, especially in the ever-evolving world of artificial intelligence. But sifting through the countless repositories to find those that combine AI and JavaScript? That’s like finding gems in a vast sea of code. Enter our Node.js web crawler—a script that automates the search, extracting repository details like name, URL, and description.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll build a crawler that taps into GitHub, hunting down repositories that work with AI and JavaScript. Let’s dive into the code and start mining those gems.&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 1: Setting Up the Project
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Initialize the Node.js Project
&lt;/h4&gt;

&lt;p&gt;Begin by creating a new directory for your project and initializing it with &lt;code&gt;npm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir github-ai-crawler
cd github-ai-crawler
npm init -y

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

&lt;/div&gt;



&lt;p&gt;Next, install the necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install axios cheerio

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;axios&lt;/code&gt;&lt;/strong&gt; : For making HTTP requests to GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cheerio&lt;/code&gt;&lt;/strong&gt; : For parsing and manipulating HTML, similar to jQuery.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Part 2: Understanding GitHub’s Search
&lt;/h3&gt;

&lt;p&gt;GitHub provides a powerful search feature accessible via URL queries. For example, you can search for JavaScript repositories related to AI with this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/search?q=ai+language:javascript&amp;amp;type=repositories

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

&lt;/div&gt;



&lt;p&gt;Our crawler will mimic this search, parse the results, and extract relevant details.&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 3: Writing the Crawler Script
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;crawler.js&lt;/code&gt; in your project directory and start coding.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Import Dependencies
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const axios = require('axios');
const cheerio = require('cheerio');

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

&lt;/div&gt;



&lt;p&gt;We’re using &lt;code&gt;axios&lt;/code&gt; to fetch GitHub’s search results and &lt;code&gt;cheerio&lt;/code&gt; to parse the HTML.&lt;/p&gt;




&lt;h4&gt;
  
  
  Step 2: Define the Search URL
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const SEARCH_URL = 'https://github.com/search?q=ai+language:javascript&amp;amp;type=repositories';

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

&lt;/div&gt;



&lt;p&gt;This URL targets repositories related to AI and written in JavaScript.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2220+ FREE&lt;/strong&gt; &lt;u&gt;&lt;b&gt;&lt;strong&gt;RESOURCES&lt;/strong&gt;&lt;/b&gt;&lt;/u&gt; &lt;strong&gt;FOR DEVELOPERS!! ❤️&lt;/strong&gt; 😍🥳 &lt;strong&gt;&lt;strong&gt;(updated daily)&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1400+ Free HTML Templates&lt;br&gt;&lt;br&gt;
351+ Free News Articles&lt;br&gt;&lt;br&gt;
67+ Free AI Prompts&lt;br&gt;&lt;br&gt;
315+ Free Code Libraries&lt;br&gt;&lt;br&gt;
52+ Free Code Snippets &amp;amp; Boilerplates for Node, Nuxt, Vue, and more!&lt;br&gt;&lt;br&gt;
25+ Free Open Source Icon Libraries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Visit &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;dailysandbox.pro&lt;/a&gt; for free access to a treasure trove of resources!&lt;/p&gt;




&lt;h4&gt;
  
  
  Step 3: Fetch and Parse the HTML
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fetchRepositories = async () =&amp;gt; {
    try {
        // Fetch the search results page
        const { data } = await axios.get(SEARCH_URL);
        const $ = cheerio.load(data); // Load the HTML into cheerio

        // Extract repository details
        const repositories = [];
        $('.repo-list-item').each((_, element) =&amp;gt; {
            const repoName = $(element).find('a').text().trim();
            const repoUrl = `https://github.com${$(element).find('a').attr('href')}`;
            const repoDescription = $(element).find('.mb-1').text().trim();

            repositories.push({
                name: repoName,
                url: repoUrl,
                description: repoDescription,
            });
        });

        return repositories;
    } catch (error) {
        console.error('Error fetching repositories:', error.message);
        return [];
    }
};

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

&lt;/div&gt;



&lt;p&gt;Here’s what’s happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetching HTML&lt;/strong&gt; : The &lt;code&gt;axios.get&lt;/code&gt; method retrieves the search results page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parsing with Cheerio&lt;/strong&gt; : We use Cheerio to navigate the DOM, targeting elements with classes like &lt;code&gt;.repo-list-item&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extracting Details&lt;/strong&gt; : For each repository, we extract the name, URL, and description.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Step 4: Display the Results
&lt;/h4&gt;

&lt;p&gt;Finally, call the function and log the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(async () =&amp;gt; {
    const repositories = await fetchRepositories();
    console.log('AI-Powered JavaScript Repositories Found:', repositories);
})();

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  Part 4: Running the Crawler
&lt;/h3&gt;

&lt;p&gt;Save your script and run it with Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node crawler.js

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

&lt;/div&gt;



&lt;p&gt;You’ll see a list of AI-related JavaScript repositories, each with its name, URL, and description, neatly displayed in your terminal.&lt;/p&gt;




&lt;h3&gt;
  
  
  Part 5: Enhancing the Crawler
&lt;/h3&gt;

&lt;p&gt;Want to take it further? Here are some ideas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pagination&lt;/strong&gt; : Add support for fetching multiple pages of search results by modifying the URL with &lt;code&gt;&amp;amp;p=2&lt;/code&gt;, &lt;code&gt;&amp;amp;p=3&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtering&lt;/strong&gt; : Filter repositories by stars or forks to prioritize popular projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saving Data&lt;/strong&gt; : Save the results to a file or database for further analysis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example for saving to a JSON file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');
const saveToFile = (data) =&amp;gt; {
    fs.writeFileSync('repositories.json', JSON.stringify(data, null, 2));
    console.log('Data saved to repositories.json');
};

// Call saveToFile after fetching repositories
(async () =&amp;gt; {
    const repositories = await fetchRepositories();
    saveToFile(repositories);
})();

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  The Beauty of Automation
&lt;/h3&gt;

&lt;p&gt;With this crawler, you’ve automated the tedious task of finding relevant repositories on GitHub. No more manual browsing or endless clicking—your script does the hard work, presenting the results in seconds.&lt;/p&gt;

&lt;p&gt;Here is &lt;a href="https://blog.dailysandbox.pro/part-2-enhancing-repository-descriptions-with-openai-for-maximum-impact/" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt; of the setup!&lt;/p&gt;

&lt;p&gt;For more tips on web development, check out &lt;a href="https://dailysandbox.pro/?ref=blog.dailysandbox.pro" rel="noopener noreferrer"&gt;&lt;strong&gt;DailySandbox&lt;/strong&gt;&lt;/a&gt; and sign up for our &lt;strong&gt;free newsletter&lt;/strong&gt; to stay ahead of the curve!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>node</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
