3. Styling with flowbite

As we know, tailwind is a CSS framework packed with classes corresponding to actual CSS. Similarly, we have Flowbite which is a package built on top of tailwind. Flowbite provides a set of pre-built components such as cards, modals, different buttons and mostly all the basic components.

Let's add flowbite to the app so that we do not have to worry about designs and classes. This will help us better focus more on the app side.

Follow this link to install flowbite to our rails app. We recommend the usage yarn rather than npm by simply typing yarn add flowbite in the command line instead of npm install flowbite

Do not forget to import flowbite

Applying styles to button

The form that we built on the last chapter works perfectly fine. But the design and the button looks ugly. Let's first style the button. Go to flowbite website and search for button. It will give us with a bunch of buttons, a lot of options to pick from. Let's go ahead and pick up the default button. Add the default button classes to the button in the form we implemented.

The button code will look like:

<%= f.submit 'Submit', class: 'text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800' %>

Adding modal to the page

Similar to adding styles to button, we can find modal code in the flowbite website. Go ahead and search for modal in the flowbite site. Grab the button code and paste at the top of the index file. Name the button Add New Idea. We are naming the button to make it easier to navigate as it's task is to open a popup to add new idea.

The create button should look like below code:

<button data-modal-target="default-modal" data-modal-toggle="default-modal" class="block text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" type="button">
  Add New Idea
</button>

Accordingly, get the wrapper code for the modal body and put the simple form implementation under it.

<div id="default-modal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
  <div class="relative p-4 w-full max-w-2xl max-h-full">
    <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
      <%= simple_form_for Idea.new do |f| %>
        <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
          <h3 class="text-xl font-semibold text-gray-900 dark:text-white">Add New Idea</h3>
          <button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="default-modal">
            <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
              <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
            </svg>
            <span class="sr-only">Close modal</span>
          </button>
        </div>
        <div class="p-4 md:p-5 space-y-4">
          <%= f.input :description, input_html: { rows: 5, class: 'text-gray-800' } %>
          <%= f.input :author, input_html: { class: 'text-gray-800' } %>
        </div>
        <div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
          <%= f.submit 'Submit', class: 'text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800' %>
          <button data-modal-hide="default-modal" type="button" class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 focus:outline-none dark:focus:ring-red-800">Cancel</button>
        </div>
      <% end %>
    </div>
  </div>
</div>

This should make the page look a bit managed and beautiful compared to the previous implementation. In this way, any tailwind based component can be grabbed from flowbite itself. Use of flowbite results to saving a lot of time and effort. We will be using flowbite components throughout the course.

Dealing with flowbite modal issue

After submitting the form, clicking the add new button does not trigger to open the modal. Also, it renders an error in your browser console. In case, you are having errors like below, the issue is created by newer versions of flowbite after 1.8.1. It requires modal to be hidden before leaving the page. So, let's achieve this using stimulus js.

image.png

Go ahead and run rails g stimulus modal in the terminal. It should generate a stimulus js file in app\javascript\controllers\ named modal_controller.js.

Create a method named hideModal in the modal_controller and hide the modal using the code below:


import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="modal"
export default class extends Controller {
  hideModal() {
    var modalInstance = FlowbiteInstances.getInstance('Modal', 'default-modal');
    if(modalInstance) {
      modalInstance.hide();
    }
  }
}

var modalInstance = FlowbiteInstances.getInstance('Modal', 'default-modal');

In the line above, we are asking the js to find a flowbite instance of 'Modal' named 'default-modal'. After finding out modalInstance, we are just triggering the hide() method which is a flowbite provided method.

Now, we can simply trigger it by adding data-controller and data-action attributes.

  1. Navigate to the index.html.erb file under ideas folder,
  2. Wrap the button tag and the modal tag with another div,
  3. Add data-controller='modal' to the parent div,
  4. Now, add data: {action: "submit->modal#hideModal"} to the form button

The entire erb code should look like below:

<div data-controller='modal'>
  <button data-modal-target="default-modal" data-modal-toggle="default-modal" class="block text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" type="button">
    Add New Idea
  </button>

  <div id="default-modal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
    <div class="relative p-4 w-full max-w-2xl max-h-full">
      <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
        <%= simple_form_for Idea.new, data: {action: "submit->modal#hideModal"} do |f| %>
          <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
            <h3 class="text-xl font-semibold text-gray-900 dark:text-white">Add New Idea</h3>
            <button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="default-modal">
              <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
                <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
              </svg>
              <span class="sr-only">Close modal</span>
            </button>
          </div>
          <div class="p-4 md:p-5 space-y-4">
            <%= f.input :description, input_html: { rows: 5, class: 'text-gray-800' } %>
            <%= f.input :author, input_html: { class: 'text-gray-800' } %>
          </div>
          <div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
            <%= f.submit 'Submit', class: 'text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800' %>
            <button data-modal-hide="default-modal" type="button" class="text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 focus:outline-none dark:focus:ring-red-800">Cancel</button>
          </div>
        <% end %>
      </div>
    </div>
  </div>
</div>

This should fix the issue.

Lesson list