41
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

un-T factory! XAAdvent Calendar 2019

Day 25

ボタンコンポーネントからはじめるCSS設計

Last updated at Posted at 2019-12-25

はじめに

Webサイトの構築においては、ページ上に繰り返し現れる各種UIをメンテナンス性・再利用性の観点からコンポーネント化し、共通のソースコード(HTML/CSS/JavaScript)で管理することが一般的です。

数あるUIコンポーネントの中で、サイト規模の大小を問わずほとんどすべてのWebサイト(ページ)に存在すると言えるのがボタンコンポーネントです。

ボタン自体はシンプルな見た目であることが多く、一見すると実装は簡単そうに思えますが、さまざまな場面に登場することから機能やデザイン(見た目)のバリエーションが意外と多くなり、サイト制作を進めるうちにいつの間にか複雑な実装になってしまった、ということも少なくありません。

私自身、1ページ完結のLPから数1000ページに及ぶ大規模サイトまでさまざまなタイプのWebサイト制作に携わる中で、「ボタン」を設計する難しさと奥深さを日々感じています。

本記事では、ボタンコンポーネントのCSS設計において私が意識している8つのポイントをご紹介します。

ボタンコンポーネントのCSS設計で意識する8つのポイント

1. 特定のclassのみでボタンコンポーネントを表現する


<a class="button" href="#">ボタン(a要素)</a>
<button class="button" type="button">ボタン(button要素)</button>

/* OK */
.button {
  /* ボタンコンポーネントのスタイル指定 */
}

/* NG */
a.button,
button,
p.button a {
  /* ボタンコンポーネントのスタイル指定 */
}

ボタンコンポーネントは特定のclassのみで表現できるようにし、HTML要素に依存したスタイル指定は避けるようにします。

上記のOK例では、ボタンコンポーネントを表現するためのclassとして .button 1 を用意し、対象となるHTML要素が何であれ(a要素、button要素、input要素、div要素、span要素など 2 )classの付与のみでボタンコンポーネントのスタイルを適用できるようにしています。

2. a要素やbutton要素のデフォルトCSSを意識して、ボタンコンポーネントの初期スタイルを調整する

ボタンコンポーネントとして利用されることが多いa要素やbutton要素にはユーザーエージェントごとのデフォルトCSSが指定されているため、それらを踏まえて .button に対してスタイルの調整をおこないます。


.button {
  -webkit-appearance: none;
  box-sizing: border-box;
  margin: 0;
  border: 0;
  background-color: transparent;
  color: inherit;
  font-size: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

button要素のデフォルトスタイルをリセットするための -webkit-appearance: none; や、a要素のデフォルトスタイルをリセットするための color: inherit;, text-decoration: none; などを記述しています。

3. displayの値はinline-flexにする

一般的にボタンコンポーネントのdisplay値は inline-block とされる場合が多いように思いますが、自分の経験上では inline-flex がもっともスタイリングの融通が効く指定だと感じています。

ボタンの内容となるテキストやアイコンは上下左右中央寄せに配置されることが多く、そのようなレイアウトを justify-contentalign-items の指定によって簡単に実現することができます。


.button {
  -webkit-appearance: none;
  box-sizing: border-box;
  margin: 0;
  border: 0;
  background-color: transparent;
  color: inherit;
  font-size: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;

  /* 追記 */
  display: inline-flex;
  justify-content: center;
  align-items: center;
}

inline-blockとの比較

display値が inline-block の場合、例えば「ボタンが特定の高さを保ちつつ、ボタン内のテキストやアイコンは上下中央寄せにしたい」といったデザイン的な要求があった場合に paddingfont-size, line-height など複数の数値を絡めた調整が必要になり、扱いづらいコードになりがちです。

その点、display値が inline-flex であれば、min-heightalign-items を指定することで同様のデザインも簡単に実現可能です。
また、「font-size を少し変えたらボタンの高さが微妙に変わってしまった」といったことも起こりにくくなります。


.button-inline-block {
  display: inline-block;
  /* 省略 */

  font-size: 20px;
  line-height: 1;
  padding-top: 12px;
  padding-bottom: 12px;
  /* 20 + 12 + 12 = 44pxの高さを確保する */
}

.button-inline-flex {
  display: inline-flex;
  /* 省略 */

  min-height: 44px; /* 最低限の高さ44pxを保ちつつ */
  align-items: center; /* ボタンの中身は上下中央寄せ */
}

4. widthやheight、paddingなど、ボタンの大きさに影響する「固定値」の指定は慎重におこなう

1つ前のセクションで「高さ指定」の話をしたばかりですが、ボタンコンポーネントに対して width, height, padding, font-size などサイズに関する固定値を指定する際には、本当にそれがボタンのデザインルールとして妥当な実装であるかを十分に検討すべきです。

デザインの意図を考える

例えば、とあるWebサイトを制作する際、デザインカンプ内に次のようなボタンコンポーネントがあったとします。
調べてみるとボタンの横幅はだいたい200pxになっていました。
このボタンコンポーネントをCSSで実装する際、どのようなコードが妥当と言えるでしょうか?

qiita_01.png

結論から言うと、この時点ではまだボタンコンポーネントのもつデザインルールや特性が判断できないため、ボタンコンポーネントとして妥当なCSSを書くことはできません。

このデザインカンプから読み取れる範囲内でも、少なくとも下記3つのようにまったく異なるパターンのデザインルールが考えられます。

  1. ボタンの横幅は必ず200pxである
  2. ボタンの横幅はテキスト量によって伸縮し、ボタン内側に**必ず左右45pxの余白(padding)**が確保される
  3. ボタンの横幅はテキスト量によって伸縮するが、どんなにテキスト量が少ない場合でも最低200pxの横幅が確保される

個人的には、2か3のどちらかかな? と考えると思います。

CSS設計時に大切なことは、このようなデザインルールについてデザイナーの意図を確認し、それらを言語化し、実装まで落とし込むことです。

そのような検討プロセスを省略して、デザインカンプの見た目から安易に width: 200px; といった固定値を指定してしまうと、本来のデザインルールと相反するコンポーネントとなって後々の制作や運用に支障をきたす場合が多いです。

5. ボタンの種類が明確にある場合のみ、Modifierを利用して派生パターンを作成する

「コンテンツ幅100%に広がるボタン」「サイト内で重要度の高いボタン」など、ボタンの種類や意味合いが明らかに他のものと区別できる場合は、Modifierを利用して新たなclassを作成し、差分となるスタイルを指定します。

See the Pen qiita_c382cf78f8ffe6fbe65b_03 by Sho Uchida (@shouchida) on CodePen.


<a class="button button-full" href="#">コンテンツ幅100%に広がるボタン</a>
<button class="button button-primary" type="button">サイト内で重要度の高いボタン</button>

.button {
  /* 省略 */
}

.button-full {
  width: 100%;
}

.button-primary {
  background-color: red;
  color: white;
}

このようなclassを作る際も、**「どこまでが標準的なボタンコンポーネントが持っている特性(スタイル)で、どこからが例外的な特性(スタイル)なのか」**についてデザイナーと議論をし、可能な限りそれらを言語化した上で実装を進めていくことが大切です。

逆に、きちんと言語化できないことを「何となく」で実装してしまうと、それが後になって矛盾を引き起こす場合が多いと感じます。

6. marginなど、レイアウトに影響するスタイル指定はおこなわない

あらゆる場所で利用される可能性のあるコンポーネントは**「それ自身がどのようにレイアウトされるかは知らない」という状態にしておくべきであり、marginfloattext-align などレイアウトに関するスタイルをコンポーネント自身に持たせることは避けます。**


/* NG */
.button {
  margin: 30px 0 0;
  text-align: center;
}

レイアウトに関するスタイルは祖先要素にもたせるか、コンポーネント要素に固有のclassを新たに付与して、そのclassに対してスタイル指定します。


<div class="centerButton">
  <button class="button" type="button">ボタン</button>
</div>

/* 祖先(親)要素にレイアウトスタイルをもたせる */
.centerButton {
  text-align: center;
}

7. target="_blank" やPDFリンクの場合に自動的にアイコンが表示されるようにする

Webサイトのデザインルールにもよるところですが、ページ内の「別ウィンドウ(タブ)で遷移するリンク」や「PDFダウンロードリンク」に対して、それとわかるよう特定のアイコンを表示することが一般的です。

ボタンコンポーネントにもそのようなデザインルールが適用されるのであれば、属性セレクタと疑似要素を利用してアイコン表示を自動化しておきます。

これにより、個々のボタンにアイコンを追加していく作業負荷が軽減し、長期的なサイト運用におけるデザインルールのバラつきを抑える効果も期待できます。

See the Pen qiita_c382cf78f8ffe6fbe65b_01 by Sho Uchida (@shouchida) on CodePen.


.button[target="_blank"]::after {
  font-family: "Font Awesome 5 Free";
  content: "\f24d";
  margin-left: 0.4em;
}

.button[href*=".pdf"]::after {
  font-family: "Font Awesome 5 Free";
  content: "\f1c1";
  margin-left: 0.4em;
}

上記の例では、Font Awesomeによるアイコンフォントを利用して、「target 属性値が _blank である場合」と、「href 属性値に .pdf が含まれる場合」にそれぞれアイコンを表示させています。
プロジェクト独自のアイコン画像を使用する場合は、background として画像を指定し、サイズの調整をおこないます。


.button[href*=".pdf"]::after {
  content: "";
  background: url(icon_pdf.svg) no-repeat center / contain;
  width: 0.8em;
  height: 0.8em;
  margin-left: 0.4em;
}

8. disabled など非活性状態のスタイルも忘れずに実装する

フォームに含まれるボタンなど、「操作ができない(disabled)状態」のボタンもWebページには頻繁に登場するため、ボタンコンポーネントを作成する際はそれらのスタイルも忘れずに用意しておきます。

See the Pen qiita_c382cf78f8ffe6fbe65b_02 by Sho Uchida (@shouchida) on CodePen.


<button class="button" type="button" disabled>非活性状態のボタン</button>

.button[disabled] {
  pointer-events: none;
  background-color: gray;
}

ここでも属性セレクタを利用し、「disabled属性がある場合」という条件でスタイルを記述します。

おわりに

ボタンコンポーネントはひとつのWebサイト内で数多く使用されるという特徴からも、設計段階における少しの差が後々のサイト運用負荷に大きく影響する可能性があります。

「たかがボタン」ですが、本記事内で繰り返し述べたように、最適なボタンコンポーネントを作るためにはデザインの意図やルールについてデザイナーと実装者との間でしっかりと認識を合わせ、言語化していくことが非常に大切です。

そして、そのようなコミュニケーションの積み重ねがWebサイト全体の品質向上に繋がっていくと考えています。

  1. CSSの命名規則については本記事では取り扱いません。

  2. div要素やspan要素はtabキー操作時にフォーカスが当たらないため、「ボタン」として振る舞わせる場合にはtabindex属性の追加など工夫が必要です。

41
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?