Help us understand the problem. What is going on with this article?

Rails × Mountain View(マウンテンビュー)で作るCSSスタイルガイド&コンポーネント

More than 1 year has passed since last update.

はじめまして。

Mountain View導入の経緯

普段 Ruby On Railsでプロダクトを開発しており、gemで完結するCSSのスタイルガイドを探していました。

有名なgemをいくつか素振りしてみたのですが、LivingStyleGuideはスタイルガイドを簡単にカテゴリ分けできず、Hologramはメンテナンスコストが高そうに感じました。

そんな時に、Railsのビューコンポーネントをそのままスタイルガイド化できるMountainViewと言うgemを知りました。

Mountain Viewとは

https://github.com/devnacho/mountain_view

With Mountain View you create reusable components for your Rails frontend, while generating a living style guide.

image.png

Mountain Viewを使用すると、スタイルガイドを生成しつつ、Railsフロントエンド用の再利用可能なコンポーネントを作ることができます。

この記事で紹介すること

紹介しておいてなんですが、私はパフォーマンス上の理由からMountain Viewのコンポーネント機能をそれほど使っていません。
(コンポーネントはRailsのパーシャルに任せれば済む話で、どちらかと言うとスタイルガイドとしての機能が欲しい...)

この記事では私がMountain Viewを導入し、コンポーネントとして使用することを諦め、スタイルガイドとして使用することに落ち着くまでに試行錯誤した内容をまとめています。

javascriptを使ったモダンなフロントエンド開発からは遠い話になりますのでご容赦ください。

導入 ~ スタイルガイドの作成

Rails 5.2.1環境で公式のREADMEだけを参考に導入することができました。

まずは Gemfilemountain_view を追加します。

# コンポーネント機能を利用する場合はグローバルに読み込みます
gem 'mountain_view'

bundle installを実行し、routes.rb ファイルに次の行を追加します。

mount MountainView::Engine => "/mountain_view" if Rails.env.development?

下記のコマンドでMountain Viewの新しいコンポーネントを作成します。

rails generate mountain_view:component button

すると、次のようなディレクトリとファイルが作成されます。

app/
  components/
    button/
      _button.html.erb
      button.css
      button.js
      button.yml

拡張子にscssやslimなど、Railsで使っているプリプロセッサを使うことができます。

rails s でサーバーを立ち上げてみましょう。
ローカル環境からbuttonコンポーネントのスタイルガイドのページが作成されていることが確認できると思います。
http://localhost:3000/mountain_view

i18n対応

i18n対応で日本語化(default_localeをjaに変更)していた場合、Mountain Viewにja.ymlが存在しないためtitleタグの表示で下記のようなエラー出ているかもしれません。

<title>&lt;span class="translation_missing" title="translation missing: ja.mountain_view.layout.styleguide_title"&gt;Styleguide Title&lt;/span&gt;</title>

私はこちらの en.ymlファイルの内容をコピペした config/locales/mountain_view.ja.yml のようなファイルを作成しました。

buttonコンポーネントを作ってみる

ここでは bootstrap を使用してコンポーネント作りを試してみます。
Gemfile に以下を追加して bundle install します。

gem 'bootstrap'

buttonコンポーネントのscssでbootstrapを読み込みます。

components/button/button.scss
@import "bootstrap";

bootstrapのbuttonコンポーネントをMountainViewに反映させた例です。

app/components/button/_button.html.erb
<button type="button" class="btn <%= properties[:modifier] %>">
  <%= properties[:title] %>
</button>
app/components/button/button.yml
-
  :modifier: btn-primary
-
  :modifier: btn-secondary
-
  :modifier: btn-success
-
  :modifier: btn-danger
-
  :modifier: btn-warning
-
  :modifier: btn-info
-
  :modifier: btn-light
-
  :modifier: btn-dark

image.png

キャッシュの問題

Mountain ViewではCSSの更新やコンポーネントの作成を行った後にキャッシュが残ってしまい、下記のコマンドを実行しないとコンポーネントのデザインがうまく反映されないことがありました。

bin/rake tmp:cache:clear

buttonコンポーネントを使ってみる

定義したコンポーネントはrender_component メソッドを使用することで使うことができます。
第一引数にコンポーネント名、第二引数にハッシュ値を渡して使います。

<%= render_component("button", { title: "Btn Primary", modifier: "btn-primary" }) %>

コンポーネントにブロックを渡し、properties[:yield]で読み込んで使用することもできます。

app/components/button/_button.html.erb
<button type="button" class="btn <%= properties[:modifier] %>">
  <%= properties[:yield] %>
</button>
使い方.html.erb
<%= render_component("button", {modifier: "btn-primary" }) do %>
  Btn Primary
<% end %>

プレゼンターを使ってみる

コンポーネントの階層に{コンポーネント名}_component.rbファイルを追加してMountainViewコンポーネント用のプレゼンターを定義することができます。

app/
  components/
    button/
      _button.html.erb
      button.css
      button.js
      button.yml
      + button_component.rb

MountainView::Presenterを継承して使います。
プロパティのデフォルト値なども使用できます。

app/components/button/button_component.rb
class ButtonComponent < MountainView::Presenter
  properties :modifier, :title
  property :element, default: 'btn'

  def modifier_title
    title || properties[:modifier].titleize
  end
end

定義したメソッドやプロパティをコンポーネントのパーシャルで利用することができます。

app/components/button/_button.html.erb
<button type="button" class="<%= element %> <%= modifier %>">
  <%= title %>
</button>

Railsのコンポーネント管理がMountainViewで完結して最高!
...と思ったのですが、このコンポーネント機能には、後述するパフォーマンス上の問題があるようです。

MountainViewのボトルネック

MountainViewのrender_component機能をeachすると、パフォーマンスがとても残念なことになってしまいます。
これを避けるために公式READMEではMountainViewのプレゼンターでrenderメソッドをオーバーライドする方法を紹介しています。

しかし、個人的にはそれよりも素直にRailsのパーシャルを使用した方がメリットが大きいなのではないかと思います。特にrender_component機能ではパーシャルのコレクション機能が使えないのが痛いので、私はeachする要素でMountain Viewのrender_component機能は使いません。

例えば、Mountain Viewを

app/components/list_item/_list_item.html.erb
<%= properties[:list_item].title %>

こんな風に定義するよりも、Railsのパーシャルで

app/views/components/_list_item.html.erb
<%= list_item.title %>

このように定義しておけば、renderをキャッシュしてくれますし、collection機能でn+1対策もできます。

使い方.html.erb
<% @lists = List.all %>

# @listsが100個あったら100回render
<% @lists.each do |list_item| %> 
  <%= render_component("list_item", {list_item: list_item}) %>
<% end %>

# @listsの結果が100個あっても1回のrenderで済む
<%= render partial: 'components/list_item', collection: @lists, as: :list_item %>

最高のビューコンポーネントはRailsのパーシャルでした...😂
と、言うわけで私はMountain ViewをRailsコンポーネント管理用のスタイルガイドとして割り切って使うことにしました。

MountainViewをスタイルガイドとして使用する

MountainViewはデフォルトではスタイルガイドのHTMLタグが表示されません。
デフォルトで prism.js が使われているので、views/mountain_view/styleguide/show.html.erbをオーバーライドして、render_componentの記述の上部に下記のコードを追記しただけでスタイルガイドっぽくなってくれます。

app/views/mountain_view/styleguide/show.html.erb
...
<div class="mv-component__description__properties" style="margin-bottom: 20px;">
  <code class="language-html"><%= CGI::pretty("#{render_component(@component.name, component_stub.properties.clone)}") %></code>
</div>
...

image.png

また、MountainViewのサイドメニューがレスポンシブ表現に邪魔なので、メディアクエリで非表示にしてみました。

image.png

app/assets/stylesheets/mountain_view/layout.scss
.mv-main {
  @media screen and (max-width: 768px) {
    width: 100%;
    padding: 30px 0;
  }
}

.mv-sidebar {
  @media screen and (max-width: 768px) {
    display: none;
  }
}

イニシャライザでMountain Viewでグローバルに読み込みたいCSSを指定することができます。

config/initializers/mountain_view.rb
MountainView.configure do |config|
  config.included_stylesheets = ["mountain_view/layout"]
end

image.png

CSS読み込みの問題

MountainViewはディレクトリ名のコンポーネントをmountain_view.css.erbでディレクトリ名と同名のcssをまとめて読み込んでいるようです。

例えばbuttonコンポーネントでbootstrapをimportした後、別のコンポーネントを作ろうとしてみたところ、button.scssでしかimportしていないbootstrapが既に読み込まれています。

MountainViewの動作がCSS設計の方針と違った場合、私はマニュフェストファイルレイアウトファイルをオーバーライドして調整するようにしています。

プレゼンターをスタブとして使ってしまう

Tipsとして、私は既存のコードをコンポーネント化する際、一旦Mountain Viewのプレゼンターをスタブとして一旦置いてみる方法をとっています。

app/components/post/post_component.rb
class PostComponent < MountainView::Presenter
  # コンポーネント作りに必要なデータを取得
  def current_user
    User.find_by(email: 'necessary-user@exapmle.com')
  end
end

こんな感じでプレゼンターのメソッドを使うと既存のビューをコンポーネントでリプレースする工程がスムーズにいって便利です。

最後に

Railsのgemで完結するスタイルガイドを探したところ、Mountain Viewを改造しながら使っていくと言う道に辿り着きました。
もっとオススメのgemや、Mountain Viewの便利な使い方をご存知の方がいらっしゃいましたらぜひコメント欄で教えてください...🙏

furitahiroshi
フリーランスエンジニアです。【経歴】 1. 漫画家・イラストレーター, 2. WEBデザイン, 3. SE, 4. WEBアプリ開発
globis
グロービスは 1992 年の創業以来、社会人を対象とした MBA、人材育成の領域で Ed-Tech サービスを提供し、現在は日本 No.1 の実績があります。これらの資産と、さらに IT や AI を活用することで、アジア No.1 を目指しています。
http://www.globis.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした