Mimic Tailwind components in Rails using Helpers
Tags: rails, tailwindcss, javascript
Published: 2021-11-27 16:22:26

I've been toying with Rails 7 alpha for the last month or so and this is a little expansion on [My Bumpy Ride post](https://dev.to/salex/going-down-the-track-to-rail-7-0-0-alpha-it-may-be-a-bumpy-ride-1enb). It is confirmed that in Rail 7, `rails new xxx` will default to importmaps. You can still use webpacker, but it will stop being upgraded. You also have the option of using `esbuild` which is like `mini-webpacker` - you get the bundling without all the webpacker bable stuff.

Recently I've added a little `tailwindcss` to a few of my apps. I have three semi-private apps that are used by a very small group of friends. None of the apps have a large amount of Javascript and all javascript has been converted to stimulus.js. But all the apps have messy mixture of css that have been through custom sass, Zurb Foundation and W3.css

The esbuild approach will probably be the best approach to upgrading a rails 6 app, but it won't be easy. Hopefully the Rails team will come up with some tools. I actually had the best results (just testing approach) by

*  Creating a new rails 7 app using esbuild 
*  Adding my node stuff, including tailwind
*  Copying my controllers, models and views to the new app.

Lots of work. Could probably go the other way and copy the new rails app guts to the and existing app clone. Then deleting the webpacker stuff. Think I'll just wait for the upgrade!

The importmaps approach is the simplest. I had a few problems importing `stimulus-flatpickr` and getting it to work. Most of the problems came from stimulus being changed to @hotware/stimulus and getting versions to match.

That still left me with tailwind css that couldn't be modified (add my preferred colors, etc). It finally hit me that I can still write my own css in a `utility first` style and get my colors. That is provided I didn't create the crap classes that I used in the past that would eventually clash with other crap classes!

The few apps that I've added tailwind css to are actually using a mixture of W3.css and tailwind. I'm slowly converting the W3.css to tailwind when warranted. You still end up with he major complaint of tailwind - the hiedius markup.

```slim
/ markup using slim
section.bg-gray-900
  .max-w-5xl.mx-auto.px-4.py-24(class="sm:px-6 lg:px-8")
    h1.text-4xl.font-bold.tracking-wide.uppercase.text-center.text-white(class="sm:text-6xl")
      |Hello World
```

Fortunately I don't have a marketing site with all the flashing components. I have simple buttons and some card-like sections.

It finally hit me that I could use rails Helpers to make life easier and keep me from creating a bunch of css classes that are almost all the same.

Lets create a couple simple buttons and boxes.

```ruby
module ComponentsHelper
  def greenBox
    "box-border box-content m-3 p-4 bg-green-300 border-green-100 border-2 text-black"
  end

  def blueBox
    "box-border box-content m-3 p-4 bg-blue-400 border-blue-200 border-2 text-black"
  end

  def btn
    "py-1 px-2 text-black hover:text-white rounded font-lg font-bold "
  end

  def btnInfo
    btn + "bg-blue-400 hover:text-blue-200"
  end

  def btnWarn
    btn + "bg-orange hover:text-yellow-300"
  end

  def btnDanger
    btn + "bg-red-500 hover:text-red-200"
  end

  def btnSuccess
    btn + "bg-success"
  end

  def btnSecondary
    btn + "bg-secondary"
  end

  def flashAlert(type)
    case type
    when 'danger'
      return "bg-red-200 text-red-600"
    when 'info'
      return "bg-blue-200 text-blue-600"
    when 'success'
      return "bg-green-200 text-green-600"
    when 'warning'
      return "bg-yellow-400 text-yellow-800"
    else
      return "bg-gray-200 text-gray-600"
    end
  end

end
```

Just a simple rails helper using tailwind classes and a few custom colors (bg-orange, bg-success). Now my buttons are all about same and it would be easy to add others.

The markup changes to something like:

```slim
 div.mx-10.flex-grow.px-2
      button[class="#{btnInfo} mr-2" data-action="click->entryLedger#check_valid"] Validate
      button[class="#{btnWarn} mr-2"] = link_to('Cancel', session[:current_acct].present? ? account_path(session[:current_acct]) :request.headers['HTTP_REFERER'],
        class:"")
      - unless @entry.new_record? || @entry.reconciled?
        - confirm_msg = @entry.cleared? ? "Entry has been Cleared - Are you sure?" : 'Are you sure?'
        button[class="#{btnDanger} mr-2"] = link_to 'Delete Entry',@entry, data: { confirm: confirm_msg }, method: :delete 
```

Again I use slim-rail, but an erb would just replace the #{} with erb tags.

This approach may make the importmaps more acceptable.