Published on

Learn How To Build A Quick Popover Feedback With Tailwind CSS Like an Expert

Quick Popover Feedback

What is Tailwind CSS?

Tailwind CSS is a utility-first CSS framework that focuses on creating personalized user interfaces quickly. It can gives you all the building blocks you are able to create personalized designs without having to fight to override irritating opinionated styles. Also, Tailwind CSS is a highly configurable, low-level CSS framework.

The description of Quick Popover Feedback ui component

Vercel style quick popover feedback with working form with web3forms

Why use Tailwind CSS to build a Quick Popover Feedback ui component?

  • It can make the building process of Quick Popover Feedback ui component faster and more easily.
  • Enables building complex responsive layouts and components freely.
  • Minimum lines of CSS code in Quick Popover Feedback component file.

The preview of Quick Popover Feedback ui component

Free download of the Quick Popover Feedback's source code

The source code of Quick Popover Feedback ui component

<!-- 
    =======================================================================
    Name    :   Quick Popover Feedback
    Author  :   Surjith S M
    Twitter :   @surjithctly

    Get more components here πŸ‘‰ https://web3templates.com/components

    Copyright Β© 2021
    =======================================================================
 -->


<style>
    input[type="radio"]:checked+label {
        /*   @apply ring-1 ring-indigo-400  border-indigo-400; */
        --tw-border-opacity: 1;
        border-color: rgba(129, 140, 248, var(--tw-border-opacity));
        --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
        --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
        box-shadow: var(--tw-ring-offset-shadow),
            var(--tw-ring-shadow),
            var(--tw-shadow, 0 0 #0000);
        --tw-ring-opacity: 1;
        --tw-ring-color: rgba(129, 140, 248, var(--tw-ring-opacity));
    }

    .is-invalid,
    .was-validated :invalid,
    .was-validated :invalid~label {
        border-color: #dc3545;
    }

    .is-invalid,
    .was-validated :invalid:focus {
        --tw-ring-color: rgba(220, 53, 69, 0.2);
    }
</style>



<script type="module" src="https://cdn.skypack.dev/twind/shim"></script>

<script src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js" defer></script>



  <div class="flex items-center justify-center min-h-screen py-6 bg-gray-50 sm:py-12">

    <!-- // For demo purposes, I have open true by default and added open false on a time out
    // For your project, its not needed. Just use x-data="{ open: false }" instead -->

    <div class="relative" x-data=" data()" x-init="() => setTimeout(() => open = false, 2000)">
      <button @click="open= !open; if (open) $nextTick(()=>{$refs.input.focus()});"
        class="px-3 py-1 text-gray-500 bg-white rounded-md cursor-text ring-1 ring-gray-300 focus:outline-none ring-inset focus:ring-indigo-400 hover:ring-indigo-400">Feedback</button>
      <div x-show="open" @click.away="open = false"
        class="flex flex-col space-y-2 px-3 pt-1 pb-2 absolute z-10 w-[350px] min-h-[180px] bg-white top-0 left-0 rounded-md shadow-xl"
        style="	--tw-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.04), 0 10px 10px -5px rgba(0, 0, 0, 0.03);">
        <div class="text-gray-500">Feedback</div>
        <div>

          <!-- 
  =======================================================================

  This is a working feedback form. To receive email, 
  Replace "YOUR_ACCESS_KEY_HERE" with your actual Access Key below.

  Create Access Key here πŸ‘‰ https://web3forms.com/
  =======================================================================
  -->


          <form action="https://api.web3forms.com/submit" method="POST" id="form" enctype="multipart/form-data"
            @submit.prevent="submitForm($event.currentTarget)" class="needs-validation" novalidate>
            <!-- Add your access key from Web3Forms here -->
            <input type="hidden" name="apikey" value="YOUR_ACCESS_KEY_HERE" />
            <input type="hidden" name="subject" value="New Feedback for your Website" />
            <!-- {You might also want to pre-populate email if the user is logged in} -->
            <!-- <input type="hidden" name="email" value="{user.email}" /> -->
            <input type="checkbox" name="botcheck" id="" style="display: none;" />
            <div class="mb-1">
              <textarea x-ref="input" required name="message" id=""
                class="w-full min-h-[100px] max-h-[300px] h-28 border-2 border-gray-200 rounded-md p-2 focus:outline-none focus:ring-4 focus:ring-indigo-50 focus:border-indigo-300"
                placeholder="Let us know what you think..."></textarea>
            </div>
            <div class="flex items-center justify-between">
              <div class="flex gap-2">
                <div>
                  <input class="absolute opacity-0 appearance-none" required type="radio" id="happy" name="type"
                    value="happy" />
                  <label class="flex p-2 border border-gray-200 rounded-md cursor-pointer" for="happy">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" width="20" height="20" viewBox="0 0 36 36">
                      <circle fill="#FFCC4D" cx="18" cy="18" r="18" />
                      <path fill="#664500"
                        d="M18 21c-3.623 0-6.027-.422-9-1-.679-.131-2 0-2 2 0 4 4.595 9 11 9 6.404 0 11-5 11-9 0-2-1.321-2.132-2-2-2.973.578-5.377 1-9 1z" />
                      <path fill="#FFF" d="M9 22s3 1 9 1 9-1 9-1-2 4-9 4-9-4-9-4z" />
                      <ellipse fill="#664500" cx="12" cy="13.5" rx="2.5" ry="3.5" />
                      <ellipse fill="#664500" cx="24" cy="13.5" rx="2.5" ry="3.5" />
                    </svg>
                  </label>
                </div>
                <div>
                  <input class="absolute opacity-0 appearance-none" required type="radio" id="sad" name="type"
                    value="sad" />
                  <label class="flex p-2 border border-gray-200 rounded-md cursor-pointer" for="sad">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" width="20" height="20" viewBox="0 0 36 36">
                      <path fill="#FFCC4D"
                        d="M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18" />
                      <ellipse fill="#664500" cx="11.5" cy="14.5" rx="2.5" ry="3.5" />
                      <ellipse fill="#664500" cx="24.5" cy="14.5" rx="2.5" ry="3.5" />
                      <path fill="#664500"
                        d="M8.665 27.871c.178.161.444.171.635.029.039-.029 3.922-2.9 8.7-2.9 4.766 0 8.662 2.871 8.7 2.9.191.142.457.13.635-.029.177-.16.217-.424.094-.628C27.3 27.029 24.212 22 18 22s-9.301 5.028-9.429 5.243c-.123.205-.084.468.094.628z" />
                    </svg>
                  </label>
                </div>
                <div>
                  <input class="absolute opacity-0 appearance-none" required type="radio" id="bug" name="type"
                    value="bug" />
                  <label class="flex p-2 border border-gray-200 rounded-md cursor-pointer" for="bug">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" width="20" height="20" viewBox="0 0 36 36">
                      <path fill="#FFCC4D"
                        d="M2.653 35C.811 35-.001 33.662.847 32.027L16.456 1.972c.849-1.635 2.238-1.635 3.087 0l15.609 30.056c.85 1.634.037 2.972-1.805 2.972H2.653z" />
                      <path fill="#231F20"
                        d="M15.583 28.953c0-1.333 1.085-2.418 2.419-2.418 1.333 0 2.418 1.085 2.418 2.418 0 1.334-1.086 2.419-2.418 2.419-1.334 0-2.419-1.085-2.419-2.419zm.186-18.293c0-1.302.961-2.108 2.232-2.108 1.241 0 2.233.837 2.233 2.108v11.938c0 1.271-.992 2.108-2.233 2.108-1.271 0-2.232-.807-2.232-2.108V10.66z" />
                    </svg>
                  </label>
                </div>
                <div>
                  <input class="absolute opacity-0 appearance-none" required type="radio" id="idea" name="type"
                    value="idea" />
                  <label class="flex p-2 border border-gray-200 rounded-md cursor-pointer" for="idea">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" width="20" height="20" viewBox="0 0 36 36">
                      <path fill="#FFD983"
                        d="M29 11.06c0 6.439-5 7.439-5 13.44 0 3.098-3.123 3.359-5.5 3.359-2.053 0-6.586-.779-6.586-3.361C11.914 18.5 7 17.5 7 11.06 7 5.029 12.285.14 18.083.14 23.883.14 29 5.029 29 11.06z" />
                      <path fill="#CCD6DD"
                        d="M22.167 32.5c0 .828-2.234 2.5-4.167 2.5-1.933 0-4.167-1.672-4.167-2.5 0-.828 2.233-.5 4.167-.5 1.933 0 4.167-.328 4.167.5z" />
                      <path fill="#FFCC4D"
                        d="M22.707 10.293c-.391-.391-1.023-.391-1.414 0L18 13.586l-3.293-3.293c-.391-.391-1.023-.391-1.414 0s-.391 1.023 0 1.414L17 15.414V26c0 .553.448 1 1 1s1-.447 1-1V15.414l3.707-3.707c.391-.391.391-1.023 0-1.414z" />
                      <path fill="#99AAB5" d="M24 31c0 1.104-.896 2-2 2h-8c-1.104 0-2-.896-2-2v-6h12v6z" />
                      <path fill="#CCD6DD"
                        d="M11.999 32c-.48 0-.904-.347-.985-.836-.091-.544.277-1.06.822-1.15l12-2c.544-.098 1.06.277 1.15.822.091.544-.277 1.06-.822 1.15l-12 2c-.055.01-.111.014-.165.014zm0-4c-.48 0-.904-.347-.985-.836-.091-.544.277-1.06.822-1.15l12-2c.544-.097 1.06.277 1.15.822.091.544-.277 1.06-.822 1.15l-12 2c-.055.01-.111.014-.165.014z" />
                    </svg>
                  </label>
                </div>
              </div>
              <div class="flex items-center space-x-2">
                <div id="thenga"> <label for="attachment"
                    :title="(files && files.length > 0) ? files.map(file => file.name).join(', ') : 'Choose a file...'"
                    class="relative flex items-center justify-center w-6 h-6 cursor-pointer">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-gray-400" width="20" height="20"
                      fill="none" viewBox="0 0 24 24" stroke="currentColor">
                      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                        d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" />
                    </svg>
                    <div id="badge"
                      class="absolute z-10 items-center justify-center w-4 h-4 text-xs font-bold text-white bg-red-500 rounded-full -right-1 -top-1"
                      :class="(files && files.length > 0) ? 'flex' : 'hidden'">
                      <span x-text="files?.length > 0 ? files.length : '0'">1</span></div>
                    <input type="file" id="attachment" name="attachment" accept="image/*"
                      x-on:change="files = Object.values($event.target.files); checkMaxSize($event.target.files) "
                      class="absolute w-px h-px overflow-hidden opacity-0 appearance-none ">
                  </label></div>
                <button type="submit"
                  class="flex justify-center w-24 px-2 py-2 text-white bg-indigo-500 rounded-md focus:outline-none focus:ring focus:ring-indigo-300"
                  :disabled="submitting" id="submit">

                  <svg x-show="success" class="w-6 h-6">
                    <use href="#success" /></svg>
                  <svg x-show="error" class="w-6 h-6">
                    <use href="#error" /></svg>
                  <span x-show="!success && !error" x-text="submitting ? 'Sending...' : 'Send'">Send</span>
                </button>
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>

  <script>
    function data() {
      return {
        open: true,
        files: null,
        submitting: false,
        success: false,
        error: false,
        checkMaxSize(files) {
          //console.table(files);
          if (files.length > 0) {
            {
              for (const [key, value] of Object.entries(files)) {
                const fileSize = value.size / 1024;
                if (fileSize > 500) {
                  console.log(fileSize);
                  alert('Please upload image less than 500 KB');
                  this.files = null;
                  return;
                }
              }
            }
          };
        },
        async submitForm(form) {
          console.log(form);
          form.classList.add("was-validated");
          if (!form.checkValidity()) {
            console.log("Please fill all the fields");
            form.querySelectorAll(":invalid")[0].focus();
          } else {
            const formData = new FormData(form);
            this.submitting = true;
            // needed for converting to JSON app/json
            // const object = {};
            // formData.forEach((value, key) => {
            //   object[key] = value;
            // });
            // const json = JSON.stringify(object);

            // console.log(object);

            try {
              const response = await fetch("https://api.web3forms.com/submit", {
                method: "POST",
                body: formData
              });

              let json = await response.json();
              this.submitting = false;

              if (response.status == 200) {
                this.success = true;
                form.reset();
                this.files = null;

              } else {
                this.error = true;

              }
            } catch (error) {
              console.log(error);
            }

            form.classList.remove("was-validated");
            setTimeout(() => {
              this.open = false;
              this.error = false;
              this.success = false;
              this.files = null;
            }, 5000);

          }

        }
      }
    }
  </script>

  <div class="hidden">
    <!-- Hidden SVG will show when it success -->
    <svg id="success" xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-white" width="24" height="24" fill="none"
      viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
        d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
    </svg>
    <svg id="error" xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-white" width="24" height="24" fill="none"
      viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
        d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
    </svg>
  </div>

How to build a Quick Popover Feedback with Tailwind CSS?

Install tailwind css of verion 2.1.0

Use the script html tag to import the script of Tailwind CSS of the version 2.1.0

<script src="https://cdn.tailwindcss.com"></script>

All the unility class needed to build a Quick Popover Feedback component

  • flex
  • min-h-screen
  • py-6
  • bg-gray-50
  • sm:py-12
  • relative
  • px-3
  • py-1
  • text-gray-500
  • bg-white
  • flex-col
  • pt-1
  • pb-2
  • absolute
  • z-10
  • w-[350px]
  • min-h-[180px]
  • top-0
  • left-0
  • mb-1
  • w-full
  • min-h-[100px]
  • max-h-[300px]
  • h-28
  • border-2
  • border-gray-200
  • p-2
  • focus:border-indigo-300
  • gap-2
  • w-5
  • h-5
  • w-6
  • h-6
  • text-gray-400
  • w-4
  • h-4
  • text-xs
  • text-white
  • bg-red-500
  • -right-1
  • -top-1
  • hidden
  • w-px
  • h-px
  • overflow-hidden
  • w-24
  • px-2
  • py-2
  • bg-indigo-500

49 steps to build a Quick Popover Feedback component with Tailwind CSS

  1. Use flex to create a block-level flex container.

  2. Set the minimum width/height of an element using the min-h-screen utilities.

  3. Control the vertical padding of an element to 1.5rem using the py-6 utilities.

  4. Control the background color of an element to gray-50 using the bg-gray-50 utilities.

  5. Control the vertical padding of an element to 3rem at only small screen sizes using the sm:py-12 utilities.

  6. Use relative to position an element according to the normal flow of the document.

  7. Control the horizontal padding of an element to 0.75rem using the px-3 utilities.

  8. Control the vertical padding of an element to 0.25rem using the py-1 utilities.

  9. Control the text color of an element to gray-500 using the text-gray-500 utilities.

  10. Control the background color of an element to white using the bg-white utilities.

  11. Use flex to create a block-level flex container.

  12. Control the padding on top side of an element to 0.25rem using the pt-1 utilities.

  13. Control the padding on bottom side of an element to 0.5rem using the pb-2 utilities.

  14. Use absolute to position an element outside of the normal flow of the document, causing neighboring elements to act as if the element doesn’t exist.

  15. Control the stack order (or three-dimensional positioning) of an element to 10 in Tailwind, regardless of order it has been displayed, using the z-10 utilities.

  16. Use w-[350px] to set an element to a fixed width([350px]).

  17. Set the minimum width/height of an element using the min-h-[180px] utilities.

  18. Use the top-0 utilities to set the top position of a positioned element to 0rem.

  19. Use the left-0 utilities to set the left position of a positioned element to 0rem.

  20. Control the margin on bottom side of an element to 0.25rem using the mb-1 utilities.

  21. Use w-full to set an element to a 100% based width.

  22. Set the minimum width/height of an element using the min-h-[100px] utilities.

  23. Set the maximum width/height of an element using the max-h-[300px] utilities.

  24. Use h-28 to set an element to a fixed height(7rem).

  25. Control the border color of an element to 0.5rem using the border-2 utilities.

  26. Control the border color of an element to gray-200 using the border-gray-200 utilities.

  27. Control the padding on all sides of an element to 0.5rem using the p-2 utilities.

  28. Control the border color of an element to indigo-300 using the focus:border-indigo-300 utilities on focus.

  29. To specify the width between columns, you can use the gap-2 utilities.

  30. Use w-5 to set an element to a fixed width(1.25rem).

  31. Use h-5 to set an element to a fixed height(1.25rem).

  32. Use w-6 to set an element to a fixed width(1.5rem).

  33. Use h-6 to set an element to a fixed height(1.5rem).

  34. Control the text color of an element to gray-400 using the text-gray-400 utilities.

  35. Use w-4 to set an element to a fixed width(1rem).

  36. Use h-4 to set an element to a fixed height(1rem).

  37. Control the text color of an element to xs using the text-xs utilities.

  38. Control the text color of an element to white using the text-white utilities.

  39. Control the background color of an element to red-500 using the bg-red-500 utilities.

  40. Use the -right-1 utilities to set the right position of a positioned element to -0.25rem.

  41. Use the -top-1 utilities to set the top position of a positioned element to -0.25rem.

  42. Use hidden to set an element to display: none and remove it from the page layout.

  43. Use w-px to set an element to a fixed width(px).

  44. Use h-px to set an element to a fixed height(px).

  45. Use overflow-hidden to clip any content within an element that overflows the bounds of that element.

  46. Use w-24 to set an element to a fixed width(6rem).

  47. Control the horizontal padding of an element to 0.5rem using the px-2 utilities.

  48. Control the vertical padding of an element to 0.5rem using the py-2 utilities.

  49. Control the background color of an element to indigo-500 using the bg-indigo-500 utilities.

Conclusion

The above is a step-by-step tutorial on how to use Tailwind CSS to build a Quick Popover Feedback components, learn and follow along to implement your own components.