CSSフレームワークのススメ - BULMAの導入と覚え書き

# はじめに
Webサイトの三大要素「HTML」「JavaScript」「CSS」。
HTMLは**内容と構造**を、JavaScriptは**振る舞い**を、CSSは**表現**を受け持つと言われます。

私は普段バックエンド中心に仕事をしているエンジニアなんですが、たまにフロントをやっても、凝ったデザインのサイトの場合はCSSはデザイナーさんにより用意されていたり、引き継ぎで古の謎のCSSを魔改造していったりなどで、0からCSSをいじる機会はあまりありませんでした。

最近趣味で作っているサイトの「モダンなデザイン」のために、「表現の枠組み」たるCSSフレームワークを勉強し始めたのですが、そこでやっと、遅まきながら、CSSフレームワークを使うと「**ものすごく楽**」という当たり前といえば当たり前の事に気が付きました。

CSSフレームワークは、プログラム言語のフレームワークとは趣が異なります。
プログラミングにおいては多大な利点を享受する為に「枠組みに縛られている」と感じることもあるFWですが、デザインたるCSSにおいては、「決められたHTMLとclassだけ言うとおりにしていればそれなりに綺麗になる」という、ユニクロ感覚で使えるファストファッションだと認識を改めた次第です。

もちろんちゃんと凝ろうとすると、CSSFWも、枠組みが縛りになることもあるのかもしれません。
しかし私は、そもそもそんなに凝る発想が無いのです。
デザインって難しいですよね。

# 何故BULMA？
CSSフレームワークはBootstrapが突出して有名な以外、あまり定番がない印象がありました。
じゃあBootstrapでいいじゃんとも思うのですが、BootstrapはCSSだけでなくJavaScript（jQuery）までセットになってフレームワークが構成されています。

BULMAの特徴は、CSSだけで成立していることです。
つまり、vue.jsだろうがreactだろうが、もちろんjQueryだろうが自由に組み合わせられるわけです。

以下にCSSFWの有名所をざっと挙げてみます。

* [Bootstrap](https://getbootstrap.com/)<br>最も有名な？FW。ここまでCSSフレームワークと書いてきましたが、Webアプリケーションフレームワークとも呼ばれ、公式には「front-end component library」と紹介されています。<br>jQuery及び独自のjsが組み込み済み。MITライセンス。
* [Foundation](https://foundation.zurb.com/)<br>Bootstrapに次いで有名とのこと。シリコンバレーにあるZURBという企業が管理している。昔の記事をググると「Bootstrapより柔軟性がある」などと書かれていました。今はまた違うかもしれませんが…。<br>独自のjsが組み込み済み。MITライセンス。
* [Materialize](https://materializecss.com/)<br>Googleが提唱した「[マテリアルデザイン](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%86%E3%83%AA%E3%82%A2%E3%83%AB%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3)」を志向したFW。アメリカのカーネギーメロン大学の学生チームによって管理されているようです。<br>独自のjsが組み込み済み。MITライセンス。
* [Semantic UI](https://semantic-ui.com/)<br>HTTP、HTMLの設計者ティム・バーナーズ＝リーが提唱した「[セマンティック・ウェブ](https://ja.wikipedia.org/wiki/%E3%82%BB%E3%83%9E%E3%83%B3%E3%83%86%E3%82%A3%E3%83%83%E3%82%AF%E3%83%BB%E3%82%A6%E3%82%A7%E3%83%96)」を志向したFW。<br>独自のjsが組み込み済み。MITライセンス。
* [Pure.CSS](https://purecss.io/)<br>アメリカのYahoo!が提供する、シンプルさを志向したFW。<br>組み込みのJavaScriptは無し。[Yahoo BSD License](https://github.com/pure-css/pure-site/blob/master/LICENSE.md)。
* [BULMA](https://bulma.io/)<br>CSSのみでのフレキシブルデザイン(Flexbox)に対応したFW。<br>組み込みのJavaScript無し。MITライセンス。
* [Tailwind CSS](https://tailwindcss.com/)<br>「ボタン」などのHTML要素ではなく、「太字」などのスタイル要素で修飾することをコンセプトに作られたFW。<br>組み込みのJavaScript無し（ただしbuildの為に使われる）。MITライセンス。

色を意味別に分ける（info青/warning黄/danger赤）とかデザイン上の基本的な考え方/部品は恐らくBootstrapが始祖で、BULMAを含めた他はそれを踏襲しているのだと思います。
私がいろいろ調べた中で、JavaScript無しのシンプルなCSSフレームワークの中で、一番人気があったのがBULMAになります。

:::note info
2024年6月追記：相当今更ですが、現在では（結構前から）Tailwind CSSも一定以上の人気がある認識です。
完全な主観ですが、Tailwind CSSは部品ではなく装飾を系統立てて整理して枠組み化したものなので、自由度が高い反面、ソース/チーム管理や統一性などいろいろな意味での難易度が上がると考えています。
BULMAは楽。これに尽きる(´・ω・｀)
:::

# BULMAことはじめ
BULMAはオープンソースでSASSも配布されており、カスタマイズできます。
が、この記事ではそこは飛ばしてデフォルトのBULMAをCDNで使うことにします。

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css">
```

執筆時点の最新版は、2019-10-17にリリースされたバージョン0.8.0です。
→[公式で紹介されているCDN](https://www.jsdelivr.com/package/npm/bulma)で最新版が確認できます。
→[2024-03-22](https://twitter.com/jgthms/status/1770878047765295449)にV1.0がリリースされた模様。

## 基本的な考え方

自前で作ったCSSと同じように、HTMLに**使いたいclass属性を指定するだけ**です。
（太字にするまでもなく当たり前ですが…）

例えば`<input type="text">`に

* inputクラスを追加するだけでBULMA風になり、<br>`<input type="text" class="input">`<br>![](https://i.imgur.com/Hb98sFY.png)
* is-smallクラスを追加すると小さくなり、<br>`<input type="text" class="input is-small">`<br>![](https://i.imgur.com/TJRa9ZR.png)
* is-dangerクラスを追加すると赤くなります。<br>`<input type="text" class="input is-small is-danger">`<br>![](https://i.imgur.com/k6c9TIv.png)

基本的には、目的に従って公式サイトから欲しいclass（と必須のHTML構造）をコピペするだけで、問題なく使えると思います。
なので余り書くことも無いのですが、強いて言えば部品を組み合わせる順序があります。

* まず大きな画面構成（レイアウト）を考える
* BULMAの[Columns](https://bulma.io/documentation/columns/)及び[Layout](https://bulma.io/documentation/layout/)で大枠を作り、その中に各種コンポーネントを入れる

「レイアウトを作って部品を囲む」。これだけです。
レイアウトとかコンポーネントとか言ってますが全てBULMAのclassの便宜上の分類の事です。
BULMAの流儀に従う限り、CSSそのものには一切手を出す必要はありません。

チームで作業する場合、CSSFWに依存する利点として「古の謎のCSSが生まれない」という点もポイントです。ドキュメントやルールはFW側で用意されているわけですから。

## レイアウトの例

コメントに書いた各要素は、公式サイトのマニュアルをご参照ください。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>タイトルです</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css">
</head>
<body>

<!-- 1. ヘッダ -->
<!-- heroコンポーネント -->
<section class="hero is-dark">
  <div class="hero-body">
    <div class="container">
      <h1 class="title">
        heroを使ったタイトル
      </h1>
      <h2 class="subtitle is-6">
        ヘッダは他に「navber」があるよ
      </h2>
    </div>
  </div>
</section>

<!-- 2. 本文 -->
本文です。

<!-- 3. フッタ -->
<!-- footerコンポーネント -->
<footer class="footer">
  <div class="content has-text-centered">
    <p>
      フッタだよ.com
    </p>
  </div>
</footer>

</body>
</html>
```

上記はこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186361/c97ad256-f935-fbf7-bbf4-4de2ad6b07e3.png)

本文の上下に`<br>`入れたくなりますよね。
古いHTMLデザインでは、レイアウトのために`<br>`とか`<table>`とか使いがちです。
（私は`<br>`は今でもめんどくさくなったら使っちゃいますが…ｗ）

ここでBULMAでは、メインのレイアウトのsectionと「section等の中で使って下さい」とされているcontainerを使います。

```html
<!-- 2. 本文 -->
<!-- レイアウトのためのsection、containerコンポーネント -->
<section class="section">
  <div class="container">
    セクションとコンテナに囲まれた本文です。
  <div>
</section>
```

すると、いい感じに余白が設定されます。
**いい感じ**に設定される。フレームワークって感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186361/01d10a73-ca1b-b351-0166-7710d006c5f6.png)

逆に言うと、本文には上記のsectionと、子要素のcontainerが必須とも言えます。
このフレームワークの流儀に従えば、自分でCSSを作る必要はありません。
もっと凝りたいと思った時に、初めてカスタマイズ（SASS）や自分のCSSファイルが必要になるのだと思います。

※ 例えばsectionのpadding（上下左右の余白）は公式で[カスタマイズ用の変数が紹介](https://bulma.io/documentation/start/modular/#importing-with-custom-sass-variables)されており、SASSを使えばカスタマイズできます。

### 余談

ちょっと前によく「bootstrapっぽい」なんて言葉を耳にして、フロントに興味がなかった私はピンとこなかったのですが、これを踏まえるとその意味がよくわかります。
デフォルトのままだと、タイトルがこんな感じで、本文は○文字分空白が開いていて…なんていう、テンプレになってるんですね。

BULMAに慣れた頃に職場にBULMAを入れたいと思い、上申したらプレゼンをするはめになったことがあるのですが、その時に「利点は実装が楽で見栄えがいいことで、デザインは将来的には阿部寛のページと同じ扱いになるかもしれません」と言った記憶がありますｗ（BULMAは無事導入されました）

# 覚え書き
BULMAは公式が良く出来ているだけに？tips的な情報が少ない気がします。
ちょっとこの記事の趣旨（紹介）とはずれますが、以降は私が必要に駆られて調べたことをメモっていきます。

## section内で、適切な間隔を設けたい
contentクラスでだいたいうまくいきます。

```html
<section class="section">
	<h1 class="title">第一章</h1>
	<div class="container">
		<div class="content">
			第一節<br>
			かくかく<br>
			しかじか
		</div>
		<div class="content">
			第二節
		</div>
	</div>
</section>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186361/ae405d1b-156f-0a7f-91b5-b44ca270497d.png)

## Columnsをフレックスボックス（自動改行）モードにしたい
is-multilineを使います。
※子要素のcolumnがis-nの比率指定ではなく、固定幅かつ画面に収まらない場合に自動改行されます。
`<div class="columns is-multiline">`

## Level（水平表示レイアウト）で、モバイルモードでも自動改行してほしくない
is-mobileを使います。
`<nav class="level is-mobile">`

## [Modalウインドウ](https://bulma.io/documentation/components/modal/)の表示がズレる
jQueryの.show/hideを使ってはいけません。
is-activeクラスをaddClass/removeClassします。
ドキュメントをちゃんと読んでなかった…。

## addons付きformにラベルを付けたい
ググったら、公式のgitのissueで話題になってました。
https://github.com/jgthms/bulma/issues/777
書き込みのサンプルだとpタグの中にpタグがありますが、これはhtml5の規約違反（pタグの中にpタグは入れられない）なので、以下ではdivに変えています。

```html
<!-- 普通のfield -->
<div class="field">
	<label class="label">タイトル</label>
	<div class="control">
		<input class="input" type="text">
	</div>
	<p class="help is-info">必須、255文字以内</p>
</div>
<!-- addons付きfield -->
<div class="field">
	<label class="label">URL</label>
	<div class="control">
		<div class="field has-addons">
			<p class="control is-expanded">
				<input class="input" type="text">
			</p>
			<p class="control">
				<a class="button is-link">リンク先確認</a>
			</p>
		</div>
	</div>
	<p class="help is-info">必須</p>
</div>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186361/1ceb3129-eea7-7325-d675-0ee21d3adf1b.png)

## マルチデバイス対応（レスポンシブ）で、overflow:scrollな要素をcolumnに格納したい
columns全体と、各columnのブレイクポイントをそれぞれ設定すれば実現可能です。

```html
<!-- desktop(1024px以上)をブレイクポイントに指定 -->
<div class="columns is-desktop">
  <!-- desktop以上の場合のみhalf -->
  <div class="column is-half-desktop">
    <div class="box">
      <pre>
Qiita (キータ) は、プログラマのための技術情報共有サービスです。
プログラミングに関することをどんどん投稿して、知識を記録、共有しましょう。Qiitaに投稿すると、自分のコードやノウハウを見やすい形で残すことができます。
      </pre>
    </div>
  </div>
  <div class="column is-half-desktop">
    <div class="box">
      <pre>
Qiita (キータ) は、プログラマのための技術情報共有サービスです。
プログラミングに関することをどんどん投稿して、知識を記録、共有しましょう。Qiitaに投稿すると、自分のコードやノウハウを見やすい形で残すことができます。
      </pre>
    </div>
  </div>
</div>
```
ブラウザ領域が1024px以上の場合
こっちが難しかった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186361/073e501b-c7f9-7918-bf18-41ccd21b681a.png)
ブラウザ領域が1023px以下の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186361/123e47a0-c92e-447a-e7b5-e26cc5a60877.png)

## モーダルを開いた際に、後ろのHTMLをスクロールしないようにしたい
モーダルを開いた際に、htmlタグに「is-clipped」クラスを追加します。

```html
<html class="is-clipped">
```

例えばvueとかスコープで管理するFWだと出来ない？場合があるので、method内に素のJavaScriptを一行入れる必要があるかも。

```javascript
openMobileMenu() {
  this.bodyLock(true)
  this.isOpenMobileMenu = true
},
closeMobileMenu() {
  this.bodyLock(false)
  this.isOpenMobileMenu = false
},
bodyLock(isLock) {
  if (isLock) {
    document.getElementById('main_html').className = 'is-clipped'
  } else {
    document.getElementById('main_html').className = ''
  }
}
```

思いつくたびに更新予定。