ボタンの設計を考える

シングルクラスで色違いボタンを実装する

ボタンスタイルとclassが1対1で対応しているのでHTMLのほうは簡潔だが、CSSのほうは背景色以外全て同じで無駄が多くなってしまう。色が増えればその分だけ無駄な重複コードも増えるし、ボタンのサイズに調整が入った場合、全ての色のスタイル定義に同じ修正をしなければならない。
重複部分をグループセレクタでまとめておけば無駄は最小限に押さえられるが、サイズ違いのパターンなどが導入されると更に重複コードが増えてしまう。

<a href="#" class="btn-green">ボタン</a>
<a href="#" class="btn-pink">ボタン</a>
.btn-green {
 display: inline-block;
 min-width: 200px;
 padding: 15px 30px;
 background-color: #338833;
 color: #fff;
 text-align: center;
 text-decoration: none;
 line-height: 1.4;
}
.btn-pink {
 display: inline-block;
 min-width: 200px;
 padding: 15px 30px;
 background-color: #eb46a6;
 color: #fff;
 text-align: center;
 text-decoration: none;
 line-height: 1.4;
}

シングルクラスで色違い×サイズ違いボタンを実装する

標準サイズでも大サイズでも、色のバリエーションは同じなので、サイズごとにグループセレクタで共通部分をまとめても、今度は各サイズごとに同じ色バリエーション指定が重複してしまう。サイズが増えたり、色が増えるたびに重複箇所はどんどん増えていく。
一般的にボタンのバリエーションは比較的多彩で、しかも最初に決めたパターン以外にもどんどん新しいバリエーションが追加される可能性もあるため、最も拡張性が求められるコンポーネントの1つである。シングルクラスの場合、バリエーションが増えるたびに新たに他と被らないclass名を考えなければならず、拡張性の観点からこのことが大きな問題となる。

マルチクラスで色違いボタンを実装する

マルチクラス設計の場合、同じ大きさで色違いのボタンを「ボタンのベーススタイル」と「色バリエーション」の掛け合わせで実装する。1つのボタンを実装するのに必ず2つのclassを記述する必要があるため、HTMLのほうは若干煩雑だが、CSSは非常にスッキリしている。

<a href="#" class="btn btn--green">ボタン</a>
<a href="#" class="btn btn--pink">ボタン</a>
.btn {  /* ボタンのベーススタイル */
  display: inline-block;
  min-width: 200px;
  padding: 15px 30px;
  color: #fff;
  text-align: center;
  text-decoration: none;
  line-height: 1.4;
}
.btn--green {  /* 色バリエーション */
  background-color: #338833;
}
.btn--pink {
  background-color: #eb46a6;
}

マルチクラスで色違い×サイズ違いボタンを実装する

サイズ違いが導入された場合でも、ベーススタイル+バリエーションの掛け合わせで設計する場合は大サイズ用のスタイルを追加するだけでCSSに重複はない。ただし、HTMLのほうは掛け合わせるバリエーションが増えるごとに付与するclassがどんどん増えていくので、こちらは逆に煩雑になる。しかし、新しいバリエーションが増えても既存のclassに手を加えることなく必要なclassを追加すれば良く、掛け合わせによるclassによって様々なバリエーションを作ることができるため、このHTML側の煩雑さは「拡張性」という面ではメリットであると言える。

マルチクラスで異なる形状のボタンが追加された場合

マルチクラスでのボタン設計で1つ注意しなければならないのは、ベースとなるボタンスタイルは必ずしも1種類である必要はないという点である。

  • 両端が円形となっている
  • 白ベースにボーダー付き
  • サイズ展開されても両端は常に円形を保つ必要がある

このように、比較的差異の大きい種別のボタンが必要となった場合には、無理にModifierでバリエーションを増やすのではなく、新たなBlockとして定義することも検討してみるべき。

  • 標準ボタンからのベースの差異がある程度多いかどうか
  • 標準ボタンとは異なるバリエーション展開のパターンを持っているかどうか

この辺りを考慮してベースとなるスタイルを分けるかどうかを判断するとよい。

.rounded-btn {  /* 円形ボタン */
  display: inline-block;
  min-width: 200px;
  padding: 1em 2em;  /* サイズ展開しても円形を保つためem指定 */
  border-radius: 2em;  /* サイズ展開しても円形を保つためem指定 */
  border: 2px solid;
  background: #fff;
  color: #338833;
  text-align: center;
  text-decoration: none;
  line-height: 1.4;
}
.rounded-btn--large {  /* 円形ボタン大サイズ */
  width: 100%;
  max-width: 400px;
  font-size: 1.2em;
}

色展開の命名案

ボタンの色というのはでたらめに設定されているのではなく、多くの場合はそのボタンの「役割」に直結している
また、色やデザインが修正される場合には「役割ごと」に再検討が行われるので、例えば一番重要なユーザーアクションを促すボタン類は赤だったのでbtn–redと命名していたのに、後からデザインが変更されてオレンジに変更されたという場合、全て命名し直さなければならないか、名前はredなのに見た目はオレンジといったちぐはぐな状態に陥ってしまうのかのどちらかの問題が生じる。
そのため、ボタンの色は直接的な色の名前ではなく、そのボタンが持つ役割をベースに分類し、命名するようにしておくのが基本となる。

【役割に応じたボタン名の例】

  • btn–default:標準的なボタン全般(※標準ボタンのベースに含めてしまうのも可)
  • btn–action:購入・申込み・送信など直接的なユーザーの行動を促すボタン
  • btn–primary:特に注目してもらいたい重要なボタン
  • btn–secondary:重要度が少し落ちるが標準よりは目立たせたいボタン
  • btn–disabled:非活性のボタン(一時的に機能停止している状態)
  • btn–###-reverse:ベタ塗りのボタンの色を反転させたタイプ
  • btn–###-ghost:濃い背景色の上に乗せる前提の白線・白文字の透明タイプ

サイズ展開の命名案

ボタンの色が比較的明確に役割と直結しているのに対して、ボタンの大きさはそのボタンの役割と常に密に連動しているわけではない。したがってサイズについてはそのままサイズ展開が分かるModifier名を検討すれば良いだろう。
ただし、「大・中・小」といった分かりやすいシンプルな展開だけでなく、「特大・大・中・小・極小」のような多段階で展開される場合もあるだろうし、「幅は同じだが高さを押さえたもの」のようなバリエーションもあるだろう。サイズ展開がある場合には、どういう大きさのボタンなのかが把握しやすいModifier名を付けることが重要である。

<a href="#" class="btn btn--default">標準ボタン</a>
<a href="#" class="btn btn--default btn--small">小ボタン</a>
<a href="#" class="btn btn--default btn--large">大ボタン</a>
<a href="#" class="btn btn--default btn--compressed">圧縮ボタン</a>

【サイズ展開用の命名例】

  • btn–xsmall:極小サイズ
  • btn–small:小サイズ
  • btn–medium:中サイズ(※標準ボタンのベースに含めてしまうのも可)
  • btn–large:大サイズ
  • btn–xlarge:特大サイズ
  • btn–compressed:高さを抑える
  • btn–wide:全幅にする

レイアウト専用Blockを用意する

各セクションの末尾に配置され、比較的大きなボタンを1つまたは2つ中央配置するなど、特定のレイアウトで繰り返し配置するパターンがある場合は、それ専用のレイアウトBlockを用意しておくと使い勝手が良くなる。

<!-- ボタン1つ -->
<div class="btns-center">
 <a href="#" class="btn btn--default btn--large">TOPへ戻る</a>
</div>

<!-- ボタン2つ -->
<div class="btns-center">
 <a href="#" class="btn btn--default-reverse btn--large">修正する</a>
 <button class="btn btn--default btn--large">確認する</button>
</div>
/* 中央配置ボタン専用のレイアウト */
.btns-center {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
}
.btns-center >.btn + .btn {
 margin-top: 20px;
}
@media screen and (min-width: 768px) {
 .btns-center {
  flex-direction: row;
 }
 .btns-center >.btn + .btn {
  margin-top: 0;
  margin-left: 20px;
 }
}

配置された親Blockからレイアウトを指定する

各ページコンテンツに固有の様々なコンポネントパーツの中でボタンを使用する場合、スタイルは共通でもレイアウトは各コンポーネントごとに固有のものになっている場合が多いだろう。このような場合はボタンに親BlockのElementクラスを設定し、レイアウトはそちらに指定するようにしておく。そうすることでセレクタの詳細度を低く保つことができ、ボタン自身のスタイルとレイアウトの指定も明確に分離しておくことができる。

<div class="pickup">
 <div class="pickup__card">
  <div class="card">
   <div class="card__thumb"><img src="img/001.jpg" alt=""></div>
   <p class="card__txt">この文章はダミーです。</p>
  </div>
 </div>
 <div class="pickup__body">
  <p>このカード情報に対する説明テキストが入ります。</p>
  <p class="pickup__btn"><a href="#" class="btn btn--primary btn--small">詳細を見る</a></p>
 </div>
</div>
.pickup__btn {  /* このBlock内でのボタンレイアウトをElementとして定義 */
 margin-top: 20px;
 text-align: right;
}

ユーティリティclassで指定する

特定のプロパティを任意に適用できるようにユーティリティclassで必要なレイアウトを組み上げる方法である。本来BEMはユーティリティclassの使用は想定していないが、実際の案件で全くユーティリティclassをを使わずに構築するのもなかなか難しいので、わざわざ専用のBlockを作るまでもない単発のスタイルを適用したい場合などにはある程度の利用を許可して良いのではないかと思われる。
ユーティリティを利用するのは、特に規則性がなく、単発で特定のプロパティを適用したいといったケースで最小限の利用に留めておいたほうが無難である。

【ユーティリティ(抜粋)】
ユーティリティclassは元のスタイルを確実に上書きすることが求められる場面が多いため、例外的にユーティリティには!importantを使うことが一般的。

<div class="ta-l mt20">  <!-- 左寄せ -->
 <a href="#" class="btn btn--default">ボタン</a>
</div>
<div class="ta-c mt20">  <!-- 中央寄せ -->
 <a href="#" class="btn btn--default">ボタン</a>
</div>
<div class="ta-r mt20">  <!-- 右寄せ -->
 <a href="#" class="btn btn--default">ボタン</a>
</div>
/*左右中央配置*/
.ta-l { text-align: left !important;}
.ta-c { text-align: center !important;}
.ta-r { text-align: right !important;}

/*マージン*/
.mt0 {margin-top: 0 !important;}
.mt10 {margin-top: 10px !important;}
.mt20 {margin-top: 20px !important;}
.mt30 {margin-top: 30px !important;}
.mt40 {margin-top: 40px !important;}
.mt50 {margin-top: 50px !important;}
inserted by FC2 system