<?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: Michael Synan</title>
    <description>The latest articles on DEV Community by Michael Synan (@michaelsynan).</description>
    <link>https://dev.to/michaelsynan</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%2F1008621%2Fe463f7ed-09c7-4079-b090-1c2643612480.jpeg</url>
      <title>DEV Community: Michael Synan</title>
      <link>https://dev.to/michaelsynan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michaelsynan"/>
    <language>en</language>
    <item>
      <title>The Perfect Infinite Scroll Marquee Component for Nuxt</title>
      <dc:creator>Michael Synan</dc:creator>
      <pubDate>Sun, 26 Nov 2023 18:17:19 +0000</pubDate>
      <link>https://dev.to/michaelsynan/nuxt-infinite-scroll-marquee-34b0</link>
      <guid>https://dev.to/michaelsynan/nuxt-infinite-scroll-marquee-34b0</guid>
      <description>&lt;h2&gt;
  
  
  Creating a Scrolling Marquee Component in Nuxt
&lt;/h2&gt;




&lt;p&gt;Have you ever needed a smooth, continuous scrolling marquee for your Nuxt projects? Inspired by the modern approach of Ryan Mulligan, I've created the perfect responsive Nuxt component for you. &lt;a href="https://ryanmulligan.dev/blog/css-marquee/"&gt;Ryan Mulligan's method&lt;/a&gt; serves as a great foundation for this implementation, so feel free to check it out for a more in-depth explanation of the CSS involved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features of the Scrolling Marquee Nuxt Component
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Responsive Font Sizing&lt;/strong&gt;: Ensuring that your marquee looks great on devices of all sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable Speed&lt;/strong&gt;: Adjust the scrolling speed to match the pacing of your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base Font and Message Props Options&lt;/strong&gt;: Tailor the font style and the message content to fit your design needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This component is a practical addition to your Nuxt project—simply copy the entire component and make note of the available props. With the ability to easily modify the speed, font, and message, you can integrate this marquee into various parts of your web applications, whether it's for announcements, promotions, or just some dynamic flair.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component Code
&lt;/h2&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 v-if="isFontSizeSet" class="marquee"&amp;gt;
    &amp;lt;div
      class="marquee__content"
      :style="{ animationDuration: animationSpeed }"
    &amp;gt;
      &amp;lt;span
        v-for="(letter, index) in (message + ' ').split('')"
        :key="index"
        class="marquee__item"
      &amp;gt;
        {{ letter }}
      &amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div
      class="marquee__content"
      :style="{ animationDuration: animationSpeed }"
    &amp;gt;
      &amp;lt;span
        v-for="(letter, index) in (message + ' ').split('')"
        :key="index + 1000"
        class="marquee__item"
      &amp;gt;
        {{ letter }}
      &amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&amp;lt;script setup&amp;gt;
import { computed, watchEffect, onMounted } from 'vue';

const props = defineProps({
  fontSize: {
    type: String,
    default: '16px',
  },
  message: {
    type: String,
    default: 'THIS IS THE DEFAULT MESSAGE',
  },
  speed: {
    type: Number,
    default: 10,
  },
});

const animationSpeed = computed(() =&amp;gt; `${props.speed}s`);
const isFontSizeSet = ref(false);

onMounted(() =&amp;gt; {
  watchEffect(() =&amp;gt; {
    document.documentElement.style.setProperty(
      '--baseFontSize',
      props.fontSize
    );
    isFontSizeSet.value = true;
  });
});
&amp;lt;/script&amp;gt;

&amp;lt;style&amp;gt;
.marquee {
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  overflow: hidden;
  user-select: none;
  display: flex;
  gap: var(--gap);
  --gap: 1rem;
}

.marquee__content {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-start;
  min-width: 100%;
  gap: var(--gap);
  animation: scroll linear infinite;
}

.marquee__item {
  white-space: nowrap;
  font-size: var(--baseFontSize);
  margin-right: 1px;
}

@media (max-width: 600px) {
  .marquee__item {
    font-size: calc(var(--baseFontSize) * 0.5);
  }
}

@media (min-width: 601px) and (max-width: 1024px) {
  .marquee__item {
    font-size: calc(var(--baseFontSize) * 0.75);
  }
}

@media (min-width: 1025px) {
  .marquee__item {
    font-size: calc(var(--baseFontSize) * 2);
  }
}

@keyframes scroll {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(calc(-100% - var(--gap)));
  }
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You'll notice the &lt;code&gt;baseFontSize&lt;/code&gt; value is applied inside of the &lt;code&gt;onMounted&lt;/code&gt; lifecycle hook preventing rendering before the style is set.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Inside Parent
&lt;/h2&gt;

&lt;p&gt;Then in the parent you can simply do something 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;&amp;lt;ScrollingMarquee fontSize="100px" message="My Awesome Marquee" speed="6" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find a fully working example here: &lt;a href="https://stackblitz.com/edit/nuxt-starter-emzrvl?file=app.vue"&gt;https://stackblitz.com/edit/nuxt-starter-emzrvl?file=app.vue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As someone who's spent a lot of time trying to get the perfect scrolling marquee code, I'm excited to share this component with the community. &lt;/p&gt;




&lt;p&gt;I'm always happy to connect with fellow #vue or #nuxt developers. If you find this component useful or have any suggestions for improvement, feel free to reach out to me on &lt;a href="https://www.linkedin.com/in/hellomichaelsynan/"&gt;LinkedIn&lt;/a&gt; or check out my &lt;a href="https://www.michaelsynan.com"&gt;personal website&lt;/a&gt;. Your feedback and connections are invaluable as we all strive to build a more vibrant and innovative web development community.&lt;/p&gt;




&lt;p&gt;Remember to share your experiences and insights if you integrate this component into your projects. Happy coding!&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nuxt</category>
      <category>vue</category>
    </item>
    <item>
      <title>Nuxt3 Form with Feedback</title>
      <dc:creator>Michael Synan</dc:creator>
      <pubDate>Tue, 04 Jul 2023 13:47:03 +0000</pubDate>
      <link>https://dev.to/michaelsynan/nuxt3-form-with-feedback-25b9</link>
      <guid>https://dev.to/michaelsynan/nuxt3-form-with-feedback-25b9</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/michaelsynan/how-to-create-a-contact-form-using-nuxt3-and-supabase-1o4c"&gt;previous post&lt;/a&gt; I demonstrated how to submit form content to &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; so you can start building your email list. Below, I will show how to add some basic validation and user feedback to help make sure the user is submitting useful information. &lt;/p&gt;




&lt;p&gt;If you're using a chrome-based browser, you can check out the Stackblitz embed, otherwise visit &lt;a href="https://stackblitz.com/edit/nuxt-starter-l3fra4?file=components%2Fcontact.vue" rel="noopener noreferrer"&gt;here&lt;/a&gt;:&lt;br&gt;
&lt;iframe src="https://stackblitz.com/edit/nuxt-starter-l3fra4?file=components%2Fcontact.vue" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The final version will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fl9oicu1cbtllr3ey27ab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fl9oicu1cbtllr3ey27ab.png" alt="Nuxt3 form with validation"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This form will use two components, the signup form itself, as well as a feedback component which receives a prop from the form indicating what type of feedback to provide upon hitting the "submit" button and performing basic validation. &lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Let's go.&lt;/strong&gt; 🏃‍♀️💨  &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Nuxt3 Template
&lt;/h2&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&amp;gt;
    &amp;lt;div class="flex items-center justify-center h-screen bg-black"&amp;gt;
      &amp;lt;div
        class="text-center text-white p-6 border-2 border-white w-full max-w-md"
      &amp;gt;
        &amp;lt;h1 class="text-2xl mb-3"&amp;gt;Join Our Newsletter&amp;lt;/h1&amp;gt;
        &amp;lt;p class="mb-6 text-lg"&amp;gt;Get amazing updates right to your inbox.&amp;lt;/p&amp;gt;
        &amp;lt;form @submit.prevent="submitEmail" class="flex flex-col"&amp;gt;
          &amp;lt;label class="mb-4 text-left text-stone-400"&amp;gt;
            Name
            &amp;lt;input
              v-model="name"
              type="text"
              class="bg-gray-800 text-white w-full px-3 py-2 mt-2"
            /&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;label class="mb-4 text-left text-stone-400"&amp;gt;
            Email
            &amp;lt;input
              v-model="email"
              type="email"
              class="bg-gray-800 text-white w-full px-3 py-2 mt-2"
            /&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;label class="flex items-center text-white mb-4"&amp;gt;
            &amp;lt;input
              type="checkbox"
              v-model="consent"
              class="mr-3"
              id="updates"
            /&amp;gt;
            I agree to terms and conditions
          &amp;lt;/label&amp;gt;
          &amp;lt;button
            type="submit"
            class="bg-white text-black px-6 py-2 mt-4"
            @click.prevent="submitForm"
          &amp;gt;
            {{ isLoading ? 'Loading...' : 'Submit' }}
          &amp;lt;/button&amp;gt;
          &amp;lt;feedback :formFeedback="formFeedback" /&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We will be using the &lt;a href="https://tailwindcss.nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt Tailwind&lt;/a&gt; module for styling. &lt;/p&gt;

&lt;p&gt;The form has 3 input fields and a submit button. Each input is modeled to a ref value and the form submit button is linked to a &lt;code&gt;submitForm&lt;/code&gt; function. &lt;/p&gt;

&lt;p&gt;Additionally, the button text is responsive to the &lt;code&gt;isLoading&lt;/code&gt; value, which we will handle in the script. Finally, you can see there is the 'feedback' component which accepts the &lt;code&gt;formFeedback&lt;/code&gt; ref value as a prop.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Nuxt3 Script Section
&lt;/h2&gt;

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

&amp;lt;script lang="ts" setup&amp;gt;
import { ref } from 'vue';

type FormFeedbackType = 'incomplete' | 'consent' | 'invalid' | 'success' | null;

const name = ref('');
const email = ref('');
const consent = ref(false);
const isLoading = ref(false);
const formFeedback: Ref&amp;lt;FormFeedbackType&amp;gt; = ref(null);
const success = ref(true);

const submitForm = async () =&amp;gt; {
  isLoading.value = true;
  formFeedback.value = null;

  if (!name.value.trim() || !email.value.trim()) {
    formFeedback.value = 'incomplete';
    isLoading.value = false;
    return;
  }

  const regex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
  if (email.value &amp;amp;&amp;amp; !regex.test(email.value)) {
    formFeedback.value = 'invalid';
    success.value = false;
    isLoading.value = false;
    return;
  }

  if (!consent.value) {
    formFeedback.value = 'consent';
    success.value = false;
    isLoading.value = false;
    return;
  }

  setTimeout(() =&amp;gt; {
    // If the execution reaches here, it means that all checks have passed.
    success.value = true;
    formFeedback.value = 'success';
    isLoading.value = false;
  }, 1000);
};
&amp;lt;/script&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;The contact form component script includes ref values for each form field and state that is required for the form to function. Underneath the refs the async &lt;code&gt;submitForm&lt;/code&gt; function which handles the validation and submission logic. In our case we are not submitting form information to an actual database so the submission status is simulated by using a &lt;code&gt;setTimeout&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;As an bonus I created a new type for the &lt;code&gt;formFeedback&lt;/code&gt; ref which only allows certain strings to get passed to the feedback component. Anything other than the options defined in the type will result in a &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; compile-time error.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Form Feedback Component
&lt;/h2&gt;

&lt;p&gt;The feedback component accepts a single prop, named 'feedbackType', which determines the type of feedback to be displayed. Based on the value of 'feedbackType', the component renders the corresponding div. The 'feedbackType' prop can only accept specific strings, as defined by the TypeScript type associated with the formFeedback ref.&lt;/p&gt;

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

&amp;lt;script lang="ts" setup&amp;gt;
const props = defineProps({
  formFeedback: String,
});
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;div
    v-if="formFeedback === 'error'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  &amp;gt;
    There was an error processing your request.
  &amp;lt;/div&amp;gt;

  &amp;lt;div
    v-else-if="formFeedback === 'success'"
    class="text-white w-full block p-3 mt-4 bg-green-500"
  &amp;gt;
    Thanks for signing up!
  &amp;lt;/div&amp;gt;

  &amp;lt;div
    v-else-if="formFeedback === 'incomplete'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  &amp;gt;
    Please complete all required fields.
  &amp;lt;/div&amp;gt;

  &amp;lt;div
    v-else-if="formFeedback === 'invalid'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  &amp;gt;
    Please enter a valid email address.
  &amp;lt;/div&amp;gt;

  &amp;lt;div
    v-else-if="formFeedback === 'consent'"
    class="text-white w-full block p-3 mt-4 bg-red-500"
  &amp;gt;
    Please agree to terms and conditions.
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;style scoped&amp;gt;&amp;lt;/style&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;This feedback component can be reused throughout your website for different forms - just make sure you update it to include different conditions. &lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  App.vue
&lt;/h2&gt;

&lt;p&gt;Maybe obvious but to be thorough, simply add the contact component to your &lt;code&gt;app.vue&lt;/code&gt; or desired page:&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&amp;gt;
    &amp;lt;contact /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;


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

&lt;/div&gt;




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

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;That's it! You can now build a contact form with validation and feedback.&lt;/strong&gt; 🎉&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;You can get the full code &lt;a href="https://stackblitz.com/edit/nuxt-starter-l3fra4?file=app.vue" rel="noopener noreferrer"&gt;here&lt;/a&gt; - simply fork the project and start working on your own as needed.&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>nuxt3</category>
      <category>vue</category>
      <category>vue3</category>
    </item>
    <item>
      <title>How to create a contact form using Nuxt3 and Supabase</title>
      <dc:creator>Michael Synan</dc:creator>
      <pubDate>Wed, 28 Jun 2023 03:34:25 +0000</pubDate>
      <link>https://dev.to/michaelsynan/how-to-create-a-contact-form-using-nuxt3-and-supabase-1o4c</link>
      <guid>https://dev.to/michaelsynan/how-to-create-a-contact-form-using-nuxt3-and-supabase-1o4c</guid>
      <description>&lt;h2&gt;
  
  
  Ready to create a simple contact form from scratch using Nuxt and Supabase?
&lt;/h2&gt;

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

&lt;p&gt;Let's go. 🏃‍♀️💨  &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Supabase
&lt;/h2&gt;

&lt;p&gt;Supabase is a great opensource alternative to Firebase that has an excellent free tier and responsive support team.&lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  Create Table and Database
&lt;/h3&gt;

&lt;p&gt;First things first, go to &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;supabase.com&lt;/a&gt; and setup a free account.&lt;/p&gt;

&lt;p&gt;Next, click "new project" from your dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fvtljpiylp9yhpa8cj76z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fvtljpiylp9yhpa8cj76z.png" alt="Supabase new project for contact form"&gt;&lt;/a&gt;&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Create new organization or choose existing one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7vqyf83otb5rg5ljseu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7vqyf83otb5rg5ljseu3.png" alt="Signup on Supabase"&gt;&lt;/a&gt;&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Create new project with strong database password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fg7vj8ozn7gb75zriwc6p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fg7vj8ozn7gb75zriwc6p.png" alt="Create database"&gt;&lt;/a&gt;&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Next click on the "Table Editor" icon on the left so you can create a new table with all of your contact form fields. Keeping it simple, just create an "email" field, and also please remember the name of your table since you will be referencing it in your code. &lt;br&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  Get API Keys
&lt;/h3&gt;

&lt;p&gt;Click on "settings" gear icon, then "API" in the left menu. From there you can copy down the project URL and public anon key, placing them in an env file in your project root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUPABASE_URL=""
SUPABASE_API_KEY=""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  Nuxt3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Contact Form Template
&lt;/h3&gt;

&lt;p&gt;Create your contact form.&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="flex items-center justify-center h-screen bg-black"&amp;gt;
    &amp;lt;div class="text-center text-white p-6 border-2 border-white w-full max-w-md"&amp;gt;
      &amp;lt;h1 class="text-2xl mb-6"&amp;gt;Join Our Newsletter&amp;lt;/h1&amp;gt;
      &amp;lt;p class="mb-4"&amp;gt;Get amazing updates right to your inbox.&amp;lt;/p&amp;gt;
      &amp;lt;form @submit.prevent="submitEmail" class="flex flex-col"&amp;gt;
        &amp;lt;label class="mb-4"&amp;gt;
          Your Email Address
          &amp;lt;input v-model="email" type="email" class="bg-gray-800 text-white w-full px-3 py-2 mt-2" /&amp;gt;
          &amp;lt;span class="text-red-500"&amp;gt;{{ emailError }}&amp;lt;/span&amp;gt;
        &amp;lt;/label&amp;gt;
        &amp;lt;button type="submit" class="bg-white text-black px-6 py-2 mt-4"&amp;gt;Send Message&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This form uses &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; classes for styling.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Install the Nuxt Supabase module
&lt;/h3&gt;

&lt;p&gt;For this project we will be using the Supabase Nuxt Module, which you can find &lt;a href="https://github.com/nuxt-modules/supabase" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&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 @nuxtjs/supabase --save-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Add refs
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const email = ref('')
const emailError = ref('')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;h3&gt;
  
  
  Now create the script section
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function submitEmail() {
  const regex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i

  if (email.value &amp;amp;&amp;amp; !regex.test(email.value)) {
    emailError.value = "Invalid email address"
    return
  } else {
    emailError.value = ""
  }

  const { error } = await client
    .from('contactForm')
    .insert([
      { email: email.value, topic: 'blog' },
    ]);

  if (error) {
    console.error("Error submitting email:", error.message);
    return;
  }

  console.log("Email submitted successfully!");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This async function does the following: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Basic validation that checks if email is formatted properly&lt;/li&gt;
&lt;li&gt;Submits 'email' ref value to your Supabase table "contactForm"&lt;/li&gt;
&lt;li&gt;Shows error or success message.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;That's it! Now you have a simple contact form that is able to make submissions to a database.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

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

&lt;p&gt;This contact form works decently if you really just want to collect email addresses, but there are lot's of other things to consider when creating a contact form. &lt;/p&gt;

&lt;p&gt;To name a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Button loading state&lt;/li&gt;
&lt;li&gt;User feedback for error and success messages&lt;/li&gt;
&lt;li&gt;Additional fields &lt;/li&gt;
&lt;li&gt;CAPTCHA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If interested in a more advanced Nuxt3 contact form using Supabase please follow or comment so you can get notified when it's published. 🤓&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>supabase</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Custom 404 Page in Nuxt</title>
      <dc:creator>Michael Synan</dc:creator>
      <pubDate>Sat, 17 Jun 2023 11:59:53 +0000</pubDate>
      <link>https://dev.to/michaelsynan/custom-404-page-in-nuxt-3ia5</link>
      <guid>https://dev.to/michaelsynan/custom-404-page-in-nuxt-3ia5</guid>
      <description>&lt;h2&gt;
  
  
  How to create a custom &lt;a href="https://nuxt.com"&gt;Nuxt3&lt;/a&gt; error page
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This solution is for projects using both the Layout and Page directory options in Nuxt and assumes you already have those directories set up. &lt;/p&gt;

&lt;p&gt;If you want to skip right to the demo, see the Stackblitz &lt;a href="https://stackblitz.com/edit/nuxt3-custom-404?file=layouts%2F404.vue"&gt;here&lt;/a&gt;. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 1.&lt;/strong&gt; First step is to create the 404 layout file inside the layout directory using the Nuxt CLI tool &lt;a href="https://nuxt.com/docs/api/commands/add"&gt;nuxi&lt;/a&gt;, as well at your error page at root.&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 add layout 404
touch error.vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: please &lt;code&gt;error.vue&lt;/code&gt; goes at root, not in pages&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2.&lt;/strong&gt; Update your error page:&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;NuxtLayout name="404"&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;div class="text-4xl"&amp;gt;You've Arrived Here on Error, boss&amp;lt;/div&amp;gt;
      &amp;lt;button class="font-bold button" @click="goBack"&amp;gt;Back&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/NuxtLayout&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
export default {
  methods: {
    goBack() {
      this.$router.push('/')
    }
  }
}
&amp;lt;/script&amp;gt;

&amp;lt;style scoped&amp;gt;
.button {
  padding: 4px 6px;
  margin: 10px 0px;
  background: black;
  color: white;
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice, the &lt;code&gt;NuxtLayout&lt;/code&gt; template accepts a prop allowing you to pass the name of your 404 page layout template. &lt;/p&gt;

&lt;p&gt;Step 3. Style your layout by adding your navbar or whatever else is appropriate for your website. &lt;/p&gt;




&lt;p&gt;Read more about Nuxt error handling &lt;a href="https://nuxt.com/docs/getting-started/error-handling"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you want to learn more about &lt;code&gt;NuxtLayout&lt;/code&gt; you can go &lt;a href="https://nuxt.com/docs/api/components/nuxt-layout#nuxtlayout"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Again, you can visit the Stackblitz here: &lt;a href="https://stackblitz.com/edit/nuxt3-custom-404?file=layouts%2F404.vue"&gt;https://stackblitz.com/edit/nuxt3-custom-404?file=layouts%2F404.vue&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>nuxt</category>
      <category>nuxt3</category>
    </item>
    <item>
      <title>Add Particles.js to Your Squarespace Site</title>
      <dc:creator>Michael Synan</dc:creator>
      <pubDate>Thu, 15 Jun 2023 00:35:56 +0000</pubDate>
      <link>https://dev.to/michaelsynan/add-particlesjs-to-your-squarespace-site-40kj</link>
      <guid>https://dev.to/michaelsynan/add-particlesjs-to-your-squarespace-site-40kj</guid>
      <description>&lt;h2&gt;
  
  
  Particles.js background on Squarespace
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://vincentgarreau.com/particles.js/"&gt;Particles.js&lt;/a&gt; is a lightweight plugin that adds motion and interaction to your Squarespace site. You can set up a Particles.js background on your site in five steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload the Javascript file&lt;/li&gt;
&lt;li&gt;Inject Particles.js and Javascript file into the header&lt;/li&gt;
&lt;li&gt;Add CSS styles&lt;/li&gt;
&lt;li&gt;Add custom code-block with target div&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's go through each of steps.&lt;/p&gt;




&lt;h3&gt;
  
  
  Upload the Javascript file
&lt;/h3&gt;

&lt;p&gt;First you should upload this file to your Squarespace site: &lt;a href="https://github.com/michaelsynan/squarespace-files/blob/main/floatingMO.js"&gt;https://github.com/michaelsynan/squarespace-files/blob/main/floatingMO.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The technique uses the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"&gt;Mutation Observer&lt;/a&gt; API to make sure the necessary script doesn't load until its target div has been rendered. To upload a file to Squarespace you need to go to "pages" -&amp;gt; new page -&amp;gt; "link" -&amp;gt; gear icon -&amp;gt; "file" -&amp;gt; "upload file" (I know its a little unintuitive). Note the link address, which is "/s/floatingMO.js" in this case. &lt;/p&gt;

&lt;h3&gt;
  
  
  Inject Javascript into your Squarespace site
&lt;/h3&gt;

&lt;p&gt;To inject your new Javascript file you need to go to "settings" -&amp;gt; "developer tools" -&amp;gt; "code injection", then place the following code in the header (remember to check your javasript file link):&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 src="https://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="/s/floatingMO.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add CSS Styles
&lt;/h3&gt;

&lt;p&gt;"design" -&amp;gt; "custom CSS"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#particles-js-wrapper {
    position: fixed;
    width: 100%;
    height: 100vh;
    top: 0;
    left: 0;
    background-color: #000;
    z-index: -1;
}

#particles-js {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 0;
}

#particles-content {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: red;
    z-index: 1;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Custom Code Block
&lt;/h3&gt;

&lt;p&gt;Go to the editor in your Squarespace site and add the following code block:&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 id="particles-js-wrapper"&amp;gt;
    &amp;lt;div id="particles-js"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to note that the code above targets the div "#particles-js" - if you want to change this you should update the JavaScript GitHub file linked to above. &lt;/p&gt;

&lt;p&gt;If you just want to play around with Particles.js outside of Squarespace, you can check out my CodePen &lt;a href="https://codepen.io/michaelsynan/pen/bGmLawL"&gt;here&lt;/a&gt;. Feel free to fork it and post your Pens below - I'll be sure to like and reply. &lt;/p&gt;

&lt;p&gt;Enjoy and feel free to reach out if you have any questions!&lt;/p&gt;

</description>
      <category>squarespace</category>
      <category>particlesjs</category>
      <category>animation</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Reveal on Scroll with Nuxt and Intersection Observer API</title>
      <dc:creator>Michael Synan</dc:creator>
      <pubDate>Sun, 23 Apr 2023 09:26:37 +0000</pubDate>
      <link>https://dev.to/michaelsynan/reveal-on-scroll-with-nuxt-and-intersection-observer-api-53di</link>
      <guid>https://dev.to/michaelsynan/reveal-on-scroll-with-nuxt-and-intersection-observer-api-53di</guid>
      <description>&lt;p&gt;While it's fun to add as many libraries to your project as possible (not really), sometimes it makes sense to use browser native solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter Web Browser APIs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of my favorite Web Browser APIs is Intersection Observer, which provides an interface for determining if an element is located inside the viewport. This handy API has been around since 2017 and has been proven to be extremely stable and performant for aiding in such tasks as lazy loading, animations and effects, and tracking user engagement. &lt;/p&gt;

&lt;p&gt;Using browser native solutions like the Intersection Observer API helps reduce load time and decreases the number of dependencies in your project. &lt;/p&gt;

&lt;p&gt;You can read more about Intersection Observer &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial I'll demonstrate how to add Intersection Observer to your Nuxt 3 project in order to trigger a reveal on scroll CSS animation. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fj2k62v4bskvvgi1opm7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fj2k62v4bskvvgi1opm7q.png" alt="Intersection Observer Illustration"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  How Does it Work?
&lt;/h3&gt;

&lt;p&gt;The Intersection Observer API works by creating a new observer instance which takes a callback function and options as its arguments. The callback function is executed whenever the target element intersects with the specified threshold in the viewport, defined in the options object. By invoking the observer's observe method and passing in the target element, you can efficiently monitor and react to its visibility changes on the page.&lt;/p&gt;

&lt;p&gt;In order to use Intersection Observer to animate an element on scroll you'll need to use the &lt;code&gt;onMounted()&lt;/code&gt; Vue.js hook. &lt;/p&gt;

&lt;p&gt;To do this, simply import &lt;code&gt;onMounted&lt;/code&gt; in the script section of your component template:&lt;/p&gt;

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

import { onMounted, ref } from 'vue';


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

&lt;/div&gt;

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

&lt;h3&gt;
  
  
  Intersection Observer
&lt;/h3&gt;

&lt;p&gt;Next, you must create your Observer instance inside of the &lt;code&gt;onMounted&lt;/code&gt; lifecycle hook and write your desired logic for any elements that get passed back, like so:&lt;/p&gt;

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

onMounted(() =&amp;gt; {
  const observer = new IntersectionObserver(
    (entries) =&amp;gt; {
      entries.forEach((entry) =&amp;gt; {
        if (entry.isIntersecting) {
          entry.target.classList.add('animate-delay');
        }
      });
    },
    {
      threshold: 0.5,
    }
  );

  if (demo) {
    observer.observe(demo);
  }
});


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

&lt;/div&gt;

&lt;p&gt;In the example above the CSS class "animate-delay" is appended to the class list of any element being observed.&lt;br&gt;
 &lt;/p&gt;

&lt;h3&gt;
  
  
  CSS styles
&lt;/h3&gt;

&lt;p&gt;With your Observer in place, you can wrap up by adding the desired CSS animations:&lt;/p&gt;

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

.animate-delay {
  animation-duration: 0.5s;
  animation-fill-mode: both;
  animation-name: animate-delay;
}

@keyframes animate-delay {
  0% {
    opacity: 0;
    transform: translateY(10px);
  }

  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

.demo {
  display: inline-block;
  opacity: 0;
  transform: translateY(10px);
}
&amp;lt;/style&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;In the demo code I added a ref to my target element called "demo" but you can use classes, IDs and any other DOM element:&lt;/p&gt;

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

&amp;lt;span ref="demo" class="demo"&amp;gt;
  Intersection Observer in Action 👌
&amp;lt;/span&amp;gt;


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

&lt;/div&gt;

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

&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If you have any issues you can always use &lt;code&gt;console.log&lt;/code&gt; inside of the Observer instance to check if your Observer instance is working correctly: &lt;/p&gt;

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

console.log('Observed element:', entry.target);


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

&lt;/div&gt;

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

&lt;h3&gt;
  
  
  Live Demo
&lt;/h3&gt;

&lt;p&gt;If you want to see it in action, you can check out the live demo on StackBlitz: &lt;a href="https://stackblitz.com/edit/intersection-observer-nuxt?file=README.md" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/intersection-observer-nuxt?file=README.md&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;In conclusion, using browser native solutions like the Intersection Observer API can greatly benefit the performance of your project by reducing load time and dependencies. The Intersection Observer API provides a stable and efficient solution for triggering animations and effects on scroll. By implementing this API in your Nuxt 3 project, you can easily create some nice animations and increase website performance. &lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>javascript</category>
      <category>animation</category>
    </item>
  </channel>
</rss>
