bemer

Build reusable UI components for Rails applications with the ability to use the BEM methodology.


Keywords
bem, bem-methodology, bemer, bemhtml, component, component-based, component-builder, component-tree, rails, reusable-components, ui-components
License
MIT
Install
gem install bemer -v 0.6.0

Documentation

Bemer

  1. Build reusable UI components for Ruby on Rails applications.
  2. Develop Ruby on Rails applications using the BEM methodology.

IMPORTANT. Using the BEM methodology is optional.

Additional resources:

  1. Habr article in Russian - ΠŸΠ΅Ρ€Π΅ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹Π΅ UI ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ Π² прилоТСниях Π½Π° Ruby on Rails.
  2. bemer-simple_form - Add the BEM methodology to your SimpleForm forms.
  3. bemer-bootstrap - Reusable UI components of Bootstrap.
  4. Ruby on Rails application using bemer and bemer-bootstrap.

Installation

Add it to your Gemfile:

gem 'bemer'

Run the following command to install it:

$ bundle

Configuration

See configuration documentation for details.

# config/initializers/bemer.rb

Bemer.setup do |config|
  config.bem                     = true
  config.modifier_name_separator = '--'
  config.path                    = 'app/frontend/components' # or Webpacker.config.source_path
  # config.default_path_prefix     = lambda { |path, view|
  #   view.controller.class.name.split('::')[0].underscore
  # }
end

Integrations

Webpacker

# config/webpacker.yml

default: &default
  source_path: app/frontend/components
  source_entry_path: ../packs
  public_output_path: frontend/assets
  # ...

development:
  <<: *default
  # ...

test:
  <<: *default
  # ...

production:
  <<: *default
  # ...

File naming and folder structure

See file naming and folder structure documentation for details.

app/
  β”œβ”€β”€ frontend/
  |     β”œβ”€β”€ components/
  |     |     β”œβ”€β”€ common/
  |     |     |     β”œβ”€β”€ carousel/
  |     |     |     |     β”œβ”€β”€ index.slim
  |     |     |     |     β”œβ”€β”€ bemhtml.slim
  |     |     |     |     β”œβ”€β”€ index.js
  |     |     |     |     β”œβ”€β”€ index.scss
  |     |     |     |     └── ...
  |     |     |     β”œβ”€β”€ form/
  |     |     |     |     β”œβ”€β”€ error_messages_elem/
  |     |     |     |     |     β”œβ”€β”€ index.slim
  |     |     |     |     |     β”œβ”€β”€ index.js
  |     |     |     |     |     β”œβ”€β”€ index.scss
  |     |     |     |     |     └── ...
  |     |     |     |     β”œβ”€β”€ locales/
  |     |     |     |     |     β”œβ”€β”€ en.yml
  |     |     |     |     |     └── ...
  |     |     |     |     β”œβ”€β”€ index.slim
  |     |     |     |     β”œβ”€β”€ base.rb
  |     |     |     |     β”œβ”€β”€ index.js
  |     |     |     |     β”œβ”€β”€ index.scss
  |     |     |     |     └── ...
  |     |     |     └── ...
  |     |     β”œβ”€β”€ admin_panel/
  |     |     |     └── ...
  |     |     β”œβ”€β”€ landing/
  |     |     |     └── ...
  |     |     β”œβ”€β”€ user_panel/
  |     |     |     └── ...
  |     |     └── ...
  |     β”œβ”€β”€ packs/
  |     |     β”œβ”€β”€ admin_panel/
  |     |     |     β”œβ”€β”€ application.js
  |     |     |     └── ...
  |     |     β”œβ”€β”€ landing/
  |     |     |     β”œβ”€β”€ application.js
  |     |     |     └── ...
  |     |     β”œβ”€β”€ user_panel/
  |     |     |     β”œβ”€β”€ application.js
  |     |     |     └── ...
  |     |     └── ...
  |     └── ...
  └── ...

Sprockets

You do not need to do anything, but add additional assets to the asset load path if necessary:

# config/initializers/bemer.rb

Bemer.setup do |config|
  config.asset_paths << Rails.root.join('some/asset/path')
end

File naming and folder structure

See file naming and folder structure documentation for details.

app/
  β”œβ”€β”€ assets/
  |     β”œβ”€β”€ javascripts/
  |     |     β”œβ”€β”€ admin_panel/
  |     |     |     β”œβ”€β”€ application.js
  |     |     |     └── ...
  |     |     β”œβ”€β”€ landing/
  |     |     |     β”œβ”€β”€ application.js
  |     |     |     └── ...
  |     |     β”œβ”€β”€ user_panel/
  |     |     |     β”œβ”€β”€ application.js
  |     |     |     └── ...
  |     |     └── ...
  |     β”œβ”€β”€ stylesheets/
  |     |     β”œβ”€β”€ admin_panel/
  |     |     |     β”œβ”€β”€ application.scss
  |     |     |     └── ...
  |     |     β”œβ”€β”€ landing/
  |     |     |     β”œβ”€β”€ application.scss
  |     |     |     └── ...
  |     |     β”œβ”€β”€ user_panel/
  |     |     |     β”œβ”€β”€ application.scss
  |     |     |     └── ...
  |     |     └── ...
  |     └── ...
  β”œβ”€β”€ frontend/
  |     β”œβ”€β”€ components/
  |     |     β”œβ”€β”€ common/
  |     |     |     β”œβ”€β”€ carousel/
  |     |     |     |     β”œβ”€β”€ index.slim
  |     |     |     |     β”œβ”€β”€ bemhtml.slim
  |     |     |     |     β”œβ”€β”€ index.js
  |     |     |     |     β”œβ”€β”€ index.scss
  |     |     |     |     └── ...
  |     |     |     β”œβ”€β”€ form/
  |     |     |     |     β”œβ”€β”€ error_messages_elem/
  |     |     |     |     |     β”œβ”€β”€ index.slim
  |     |     |     |     |     β”œβ”€β”€ index.js
  |     |     |     |     |     β”œβ”€β”€ index.scss
  |     |     |     |     |     └── ...
  |     |     |     |     β”œβ”€β”€ locales/
  |     |     |     |     |     β”œβ”€β”€ en.yml
  |     |     |     |     |     └── ...
  |     |     |     |     β”œβ”€β”€ index.slim
  |     |     |     |     β”œβ”€β”€ base.rb
  |     |     |     |     β”œβ”€β”€ index.js
  |     |     |     |     β”œβ”€β”€ index.scss
  |     |     |     |     └── ...
  |     |     |     └── ...
  |     |     β”œβ”€β”€ admin_panel/
  |     |     |     └── ...
  |     |     β”œβ”€β”€ landing/
  |     |     |     └── ...
  |     |     β”œβ”€β”€ user_panel/
  |     |     |     └── ...
  |     |     └── ...
  |     └── ...
  └── ...

Usage

Component to which BEMHTML templates cannot be applied

HTML structure of the Carousel component from Bootstrap:

/ app/frontend/components/common/carousel/index.slim

.carousel.slide data-ride="carousel" class=local_assigns[:cls]
  ol.carousel-indicators
    - image_urls.size.times do |i|
      li data-target=".carousel" class=(:active if i.zero?) data-slide-to=i
  .carousel-inner
    - image_urls.each_with_index do |image_url, i|
      .carousel-item class=(:active if i.zero?)
        = image_tag image_url, class: 'd-block w-100'
  a.carousel-control-prev data-slide="prev" data-target='.carousel' role="button"
    span.carousel-control-prev-icon aria-hidden="true"
    span.sr-only Previous
  a.carousel-control-next data-slide="next" data-target='.carousel' role="button"
    span.carousel-control-next-icon aria-hidden="true"
    span.sr-only Next

Rendering the carousel component in any view or other UI components:

= render_component :carousel, prefix: :common, image_urls: image_urls, cls: 'carousel-fade'

Component to which BEMHTML templates can be applied

Tree structure of the Carousel component from Bootstrap:

/ app/frontend/components/common/carousel/index.slim

= define_component do |component|
  = component.block :carousel, 'data-ride': :carousel, 'data-interval': false, cls: :slide do |carousel|
    = carousel.elem :indicators, tag: :ol, cls: 'carousel-indicators'
      - image_urls.size.times do |i|
        = carousel.elem :indicator, tag: :li, 'data-slide-to': i, mods: (:active if i.zero?), 'data-target': '.carousel'
    = carousel.elem :inner, cls: 'carousel-inner'
      - image_urls.each_with_index do |image_url, i|
        = carousel.elem :item, cls: 'carousel-item', mods: (:active if i.zero?)
          = carousel.elem :image, tag: :img, cls: 'd-block w-100', src: image_url
    = carousel.elem :control_prev, tag: :a, cls: 'carousel-control-prev', 'data-slide': :prev, role: :button, 'data-target': '.carousel'
      span.carousel-control-prev-icon aria-hidden="true"
      span.sr-only Previous
    = carousel.elem :control_next, tag: :a, cls: 'carousel-control-next', 'data-slide': :next, role: :button, 'data-target': '.carousel'
      span.carousel-control-next-icon aria-hidden="true"
      span.sr-only Next

Default template:

/ app/frontend/components/common/carousel/bemhtml.slim

= define_templates do |template|
  = template.elem(mods: :active).add_cls :active

Rendering the carousel component in any view or other UI components using BEMHTML templates:

= render_component :carousel, prefix: :common, image_urls: image_urls do |template|
  = template.block(:carousel).add_mix :carousel_fade

Documentation in Russian

  1. File naming and folder structure
  2. Configuration
  3. Creating and using UI components
  4. Helpers for UI components
    1. define_component
    2. define_templates
    3. render_component
    4. refine_component
    5. component_pack
    6. component_asset_path
    7. component_partial_path
  5. Additional helpers for the BEM methodology
    1. bem_attrs_for
    2. bem_mix
    3. bem_mods
    4. block_tag
    5. elem_tag
  6. BEMHTML
    1. Templates
    2. Node
    3. Predicates
    4. Modes

Links

  1. BEM methodology - https://bem.info/methodology/
  2. Minimal stack for coding client-side JavaScript and templating - https://github.com/bem/bem-core
  3. Declarative template engine for the browser and server with regular JS syntax - https://github.com/bem/bem-xjst
  4. BEM Forum - https://bem.info/forum/

License

Copyright (c) 2017-2023 Alexander Grigorev. See LICENSE.txt for further details.