DEV Community

Serhii Jun
Serhii Jun

Posted on

Not-so-ugly styles code in your Rails app

Let's say, u already normally human and didn't use tailwindcss for your Rails app. Well, I just want to show you how I uses a feature from ruby-3.2 introduced by @zverok. It names Data and I use it together with ViewComponent and Rux to have a little less ugly code.

Gemfile:

group :development, :test do
  ...
  gem 'view_component', '~> 2.82'
  gem 'rux-rails', '~> 1.2', '>= 1.2.2'
  gem 'birdel', '~> 3.1'
end
Enter fullscreen mode Exit fullscreen mode
  1. Generate new component by ViewComponent or by Birdel
$ birdel gcom Ui::Mix::BarComponent

# app/
# ├─ components/
# │  ├─ ui/
# │  │  ├─ mix/
# │  │  │  ├─ bar_component/
# │  │  │  │  ├─ bar_component.rb
# │  │  │  │  ├─ bar_component.js
# │  │  │  │  ├─ bar_component.css
# │  │  │  │  ├─ bar_component.html.erb
# │  │  │  │  ├─ bar_component_controller.js
# │  │  │  │  └─ bar_component_actor.js
Enter fullscreen mode Exit fullscreen mode
  1. remove your bar_component.html.erb file

  2. Write your styles into bar_component.css

  3. Rename bar_component.rb file to bar_component.rux

#app/components/ui/mix/bar_component/bar_component.rux
class Ui::Mix::BarComponent::BarComponent < ViewComponent::Base
  Styles = Data.define(
    :nav_bar,
    :nav_item,
    :btn,
    :btn_active,
    :nav_item__logo
  )

  attr_reader :styles
  def initialize()
  end

  def styles
    @styles ||= Styles.new(
      nav_bar:        'nav-bar',
      nav_item:       'nav-item',
      btn:            'nav-item-btn',
      btn_active:     'nav-item-btn--active',
      nav_item__logo: 'nav-item-logo'
    )
  end

  def call
    <div class={css_class} data-controller={css_class}>
      <div class={styles.nav_bar}>
        <div class="#{styles.nav_item} #{styles.nav_item__logo}">
        </div>
        <div class={styles.nav_item}>
          <div class={styles.btn} data-action="click->#{css_class}#handleLeftBtn">Left</div>
          <div class="#{styles.btn} #{styles.btn_active}">Center</div>
          <div class={styles.btn}>Right</div>
        </div>
        <div class={styles.nav_item}>
        </div>
      </div>
    </div>
  end

  def css_class
    @css_class ||= "ui--mix--blabla..."
  end
end
Enter fullscreen mode Exit fullscreen mode

Explanation:

In this part we define a custom Data class named Styles with fields, which will be used as the names of classes from our style file:

  Styles = Data.define(
    :nav_bar,
    :nav_item,
    :btn,
    :btn_active,
    :nav_item__logo
  )
Enter fullscreen mode Exit fullscreen mode

And here we just set a value for each field of our Styles data object:

  def styles
    @styles ||= Styles.new(
      nav_bar:        'nav-bar',
      nav_item:       'nav-item',
      btn:            'nav-item-btn',
      btn_active:     'nav-item-btn--active',
      nav_item__logo: 'nav-item-logo'
    )
  end
Enter fullscreen mode Exit fullscreen mode

Great now we can use that @styles object inside #call method which allows us to write the same to JSX-styled syntax code. Like:

<div class={styles.nav_item}>
Enter fullscreen mode Exit fullscreen mode

Example rendered HTML:

<div class="nav-bar">
  <div class="nav-item nav-item-logo">
  </div>
  <div class="nav-item">
    <div class="nav-item-btn" data_action="click->ui--mix--bar-component#handleLeftBtn">
      Left
    </div>
    <div class="nav-item-btn nav-item-btn--active">
      Center
    </div>
    <div class="nav-item-btn">
      Right
    </div>
  </div>
  <div class="nav-item">
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Btw, Birdel (https://github.com/serhiijun/birdel) is my microframework which is not ready yet and you can help me by contributing. It's revolutionized tool for rails assets management and WebSocket components rendering which will replace sucks rails-turbo.

Top comments (0)