NanocでTailwindCSSを使う

TailwindCSSがいいかんじなのでNanocでも使おうと言うメモ。

  • 導入方法
  • ヘルパーを使ったコンポーネント化

の二点が要旨となる。

TailwindCSSの導入

CDNから読むパターンのインストール方法で行く。

参考: Get started with Tailwind CSS

ちなみにCDNからまるっと読むのではなく、Tailwind CLIや他の仕組みのプラグインとしてコンパイルするやり方だと、使用しているユーティリティクラスのみをまとめたCSSを出力してくれる。
CSSは少しでも小さくという要件や意識の高さがあるならそちらのやり方を採用しよう。

layouts/header.html

以下の二行を追加。

<script src="https://cdn.tailwindcss.com"></script>
<script src="/js/tailwind.config.js"></script>

コンフィグによるカスタマイズが不要なら二行目はいらない。

content/js/tailwind.config.js

CDNから読んだ場合はインラインでコンフィグできるが、それを別のファイルに分けても問題なく動作する。
内容は必要に応じて。

tailwind.config = {
  theme: {
    extend: {
      fontSize: {
        "2xs": ".625rem",
      },
      colors: {
        transparent: "transparent",
        current: "currentColor",
        gray: {
          dark: "#333333",
          medium: "#999999",
          light: "#f6f6f6",
        },
        lime: "#99e599",
        blue: {
          link: "#4183c4",
        },
      },
    },
    fontFamily: {
      sans: ["Lato", "Helvetica Neue", "Arial", "Helvetica", "sans-self"],
      icons: ["Icons"],
    },
  },
  plugins: [],
};

ヘルパーにするかレイアウトにするか

TailwindCSSはコンポーネント化と合わせて使うべきCSSフレームワークなので、Nanocで使う場合もUIパーツをコンポーネントの単位に分解して記述していく。
Nanocでそれを実現する手段はヘルパーかレイアウトである。

どちらでも好みでいいかなと考えていたが、実用し始めるとレイアウトの方は致命的な問題があったのでヘルパーに落ち着いた。

一応どちらにするか検討していたころにまとめたメリットとデメリットを置いておく。

ヘルパーにした場合のメリットとデメリット

  • メリット
    • 記述が簡潔
  • デメリット
    • RubyのコードなのでTailwindCSSの補完が効かない
    • ヘルパーを変更するとすべてのファイルがリコンパイル対象となる

レイアウトファイルやMarkdown内での記述例。

<%= nav %>
<%= updated_at(@item[:updated_at]) %>

レイアウトにした場合のメリットとデメリット

  • メリット
    • HTMLなのでTailwindCSSの補完が効く(VSCodeのTailwind CSS IntelliSense)
    • 変更が影響するファイルだけがリコンパイル対象となる
  • デメリット
    • 記述がわずかに冗長
    • 変更が一部のファイルにしか反映されない場合がある

レイアウトファイルやMarkdown内での記述例。

renderを毎回書く必要があるほか、レイアウトファイルを置くディレクトリ階層も書かなければならない。やや冗長と言えるだろう。

<%= render 'components/nav' %>
<%= render 'components/updated_at', date: @item[:updated_at] %>

変更が影響するファイルだけがリコンパイル対象になるので速くていいぞ!と思っていたのだが、影響するファイルをリコンパイルしない問題があり、アカンやんとなった。

記述が冗長とは言え許容範囲ではあったし、補完が効くのもありがたかったんだけどね。

ヘルパーを使ったコンポーネント化

というわけでヘルパーとしてコンポーネントを書いて行く。

複数のコンポーネントをhelpers.rbにまとめて書いてもいいのだが、1ファイル1コンポーネントにした方が見通しがいいのでそのようにしていく(このヘルパーの実装どんなだっけな?となったときにhelpers.rbを開いてファイル内検索するより、VSCodeでCtrl+Pを押してファイル名を検索する方が速い)

コンポーネント単位でヘルパーファイルをたくさん作る準備

方針からしてlib以下にhelpersディレクトリを掘って、その下にコンポーネントファイルをたくさん作りたい。

Nanocはいつのバージョンからか、lib以下のファイルを自動で読み込むようになっている。
深いディレクトリ構造でもすべて読み込むので、自分で頑張ってrequireやrequire_relativeを書いたりする必要はない。

ただひとつ注意点があり、この読み込みは「アルファベット順で読み込まれる」こと。
そのため少しバッドノウハウ的な変更を行う。

  • lib以下にhelpersディレクトリを作る
  • helpers.rbをhelpers_.rbにリネームする

うげってなるかもしれないけど、これ公式ドキュメントに書いてあることなんだよ。

Nanoc » Helpers

lib/helpers/ui_components.rb

一応お作法としてモジュールにまとめる。

module UIComponents
end

ここではUIComponentsというモジュールでざっくりまとめてしまっているが、もっと細分化したい場合は適宜モジュールを増やしてもいい。

ちなみにモジュールで囲まずグローバル空間に直接ヘルパーを定義しても動くことは動く。

lib/helpers/ui_components/*.rb

UIComponentsに属しているヘルパーをそれとわかるディレクトリにまとめるぞと。
そしてそのファイルにコンポーネント的なヘルパーを書いて行く。

lib/helpers_.rb

定義したモジュールを使いますよという宣言を追加する。

use_helper UIComponents

helpers.rbのままだとUIComponentsモジュールは見つからないぞと言われてしまう。
(helpers.rbのあとにhelpers以下を読み込むので)

コンポーネントを書いて行く

コンポーネントの例。

lib/helpers/ui_components/page_title.rb

ページのタイトルを表示する。

module UIComponents
  def page_title(title)
    "<h2 class='text-3xl mt-5 mb-2 pt-2.5 text-gray-dark font-bold font-sans'>#{title}</h2>"
  end
end

lib/helpers/ui_components/updated_at.rb

最終更新日時を表示する。

module UIComponents
  def updated_at(date)
    "<div class='mt-4 pb-4 text-right text-gray-medium'>最終更新: #{date}</div>"
  end
end

lib/helpers/ui_components/item_tags.rb

タグを横並びに表示する。

module UIComponents
  def item_tags(item)
    "<ul class='mt-4 flex flex-wrap max-w-3xl'>" + item[:tags].map { |tag| "<li class='mt-1 mr-1 px-2.5 py-0.5 inline-block text-2xs rounded-sm font-bold bg-gray-100 text-gray-medium break-keep'>#{tag}</li>" }.join("") + "</ul>"
  end
end

TailwindCSSの良さ

ある箇所のスタイル変更したい、となったときはコンポーネントファイルを開けばすぐ修正できるので快適である。

当たってるクラスを見てー、そのクラスの定義をCSSファイルの中から探してー、とかやらなくていい。
CSSクラスの名前を考えたり覚えたりする必要もない。

スタイルの局所化すばらしい。

非JSのフレームワークや仕組みに組み込みやすいのもTailwindCSSのいいところ。