[CSS] button要素のスタイルシート、最新テクニックを徹底解説

<button></button>は最も使用されている要素の一つです。フォームの送信、モーダルを開く時、データを表示する時など、インタラクションのトリガーとして使用されています。button要素の詳細とすべてのブラウザで機能するようにスタイルを完璧に定義する方法について紹介します。

さらに、よく使用されるボタンのスタイル方法をはじめ、ボタンの実装時にある落とし穴の解決方法についても明らかにします。

button要素のスタイルシートを徹底解説

Styling The Good Ol' Button Element
by Ahmad Shadeed

下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。

button要素のデフォルトのスタイル

まず最初に、button要素に適用されているブラウザのデフォルトのスタイルを見てましょう。下記は、Google Chromeのユーザーエージェントスタイルシートにおけるbutton要素のスタイルです。
参考: 各ブラウザごとのデフォルトのスタイルシート

デフォルトのボタンは、下記のように表示されます。

デフォルトのボタンの見え方

デフォルトのボタンの見え方

ボタンのスタイルを定義するためには、最初にappearanceプロパティを上書きする必要があります。これはOSに基づくプラットフォーム固有のスタイルを提供するために使用されます。

appearanceプロパティをnoneで上書きすると、appearanceが削除されます。

サイトのキャプチャ

appearance: none;を適用したボタン

次に、ボタンの実際のスタイル定義を始める前に、いくつかのリセットが必要です。例えば、ボーダーや背景や角丸です。

ボタンをリセットした状態

ボタンをリセットした状態

これでボタンのデフォルトのスタイルをリセットしたので、提供されたデザインに基づいてボタンにスタイルを適用する方法について解説します。

CSSのリセットについて詳しくは、下記をご覧ください。

button要素の基本的なデザイン

基本的なデザインの例として、簡単なデザインのボタンから始めます。
ボタンの構造は次のとおりです。

ボタンの基本的なデザインと構造

ボタンの基本的なデザインと構造

上記のデザインになるように、CSSを記述します。

次に、ボタンの状態(ホバー・フォーカス・無効)について説明します。

ボタンの状態

ボタンの状態

ホバーとフォーカスのスタイル

ボタンがホバーされたことをユーザーに伝えるには、そのインタラクションのスタイルを追加することが重要です。キーボードユーザーのフォーカススタイルにも同じコンセプトが適用されます。

Ire Aderinokunの記事にもあるように、ホバーとフォーカスの疑似クラスを追加する際にはその順番が重要です。

focusの前にhoverのスタイルを定義すると、各スタイルは正しく機能します。逆に、hoverの前にfocusを定義してしまうと、要素をマウスでクリックした際にfocusのスタイルは表示されず、hoverのスタイルのみが表示されてしまいます。

focusとhoverの順番

記述の正しい順番は、focusの前にhoverのスタイルを定義する

:hoverと:focusの正しい順序は下記のとおりです。

ボタンの最小幅

ボタンの幅が適切であることを確認するには、ボタンのラベルが短い場合と長い場合を考慮することが重要です。min-widthプロパティは、ボタンの最小幅を確保するのに役立ちます。
参考: Min and Max Width/Height in CSS
翻訳: min-widthとmax-width、min-heightとmax-heightの効果的な使い方のまとめ

下記の画像では、ラベルの長さが異なるボタンが英語とアラビア語の両方で表示されています。ボタンに最小幅を定義しないと、ラベルが短い場合にボタンの幅が狭くなりすぎることがあります。こういった場合は、min-widthを使用して回避することをお勧めします。

上: min-widthなし、下: min-widthあり

上: min-widthなし、下: min-widthあり

ボタン内のpadding

ボタンに幅があり、両側に十分なスペースがあるため、ボタンに水平方向のpaddingを追加する必要がないように思うかもしれません。しかし、ラベルが変更された場合に困ります。

変更されてラベルが長くなった場合を見てましょう。

ボタンのラベルが長い場合

ボタンのラベルが長い場合も想定しておく

ボタン内のスペースはwidthで確保するのではなく、paddingで確保するようにします。もちろん、前述のmin-widthと組み合わせて、padding+min-widthで定義します。これでラベルの長さが予期しない場合でも保証されます。

ボタンのフォントファミリー

デフォルトでは、フォーム要素は<html>または<body>要素に適用されているフォントファミリーを継承しません。

この問題を解決するには、フォーム要素にfont-family: inherit;を定義する必要があります。

ボタンの無効状態

ボタンが無効であることを示すには、button要素にdisabled属性を追加し、CSSでスタイルを定義できます。

ボタンが無効になっている場合、キーボードでもフォーカスを合わせることができず、アクセシビリティツリーからも削除されます。

ホバー時のカーソル

ボタン要素のデフォルトのカーソルは、矢印です。これがなぜなのか、Stackoverflowでの回答が参考になります。

ボタンは伝統的なデスクトップソフトウェアのUIコントロールで、インターネットが出現するまではハンドポインターが使われたことのないコンテキストです。Webページで同じコントロールが使用され始めたとき、デスクトップ環境と同じボタンが使用されるようになりました。

このビヘイビアを無効にするには、デフォルトのカーソルをハンドにすることをお勧めします。

ホバー時のカーソル

ホバー時のカーソルをハンドに変更する

これでbutton要素の基本的なデザインのスタイルが完成しました。
ここまでのCSSは下記の通りです。

次に、さまざまなプロジェクトで使用できるボタンのさまざまなスタイルとタイプについて解説します。

アイコン付きのボタン

目立たせたり、メッセージを伝えるために、アイコン付きのボタンが必要になることがあります。重要なポイントは、アイコンだけにアクセスできないように実装することです。

アイコン付きのボタン

アイコン付きのボタン

ここで紹介するアイコン付きのボタンは、アクセシビリティツリーから非表示されるように、アイコンにaria-hidden属性を追加しています。また、IEでアイコンだけがフォーカスされないようにfocusable=falseも追加します。

ボタンにアイコンを加える時は、vertical-align: middle;でコンテンツの中央に揃えておくことに注意してください。

また、テキストを非表示にしてアイコンのみのボタンにしたい場合があります。その場合は、テキストをspan要素で内包し、display: none;を与えます。

これでテキストは非表示され、アイコンだけを表示したボタンになりますが、アクセシビリティは非常に悪いです。この実装ではスクリーンリーダーでボタンにアクセスできなくなります。
これにはいくつかの解決策があります。

SVGアイコンを使用する

ボタンにSVGアイコンを使用する際に以前は単独のSVGファイルでしたが、すべてのSVGアイコンを1つのファイルに結合してから、各アイコンをSVGの<use>要素で呼び出すことをお勧めします。

この記述で、#facebookのアイコンが呼び出されています。こうすることで、コードの重複を減らすことができます。

アイコン付きボタンの幅を設定する方法

ボタンのテキストを非表示にすると、min-widthがあるため、ボタンの幅はアイコンのサイズに最適化されません。この問題を解決するには、アイコン付きボタンの幅を独自に定義します。

サイトのキャプチャ

左: min-widthあり、右: min-widthなし

テキストを視覚的に隠す

テキストを視覚的に隠す際によく使用される.sr-onlyで、要素をスクリーンから視覚的に非表示にできますが、スクリーンリーダーからはアクセスできます。

※.sr-onlyとは、Bootstrapのアクセリビリティ用のclassです。

font-sizeを0にする

font-sizeを0にすると、テキストを配置したspan要素はスペースを取りません。実質的に隠された状態になります。

ただし、.sr-onlyを使った方法の方が理にかなっていると思います。font-sizeはちょっとしたハックでしょう。

aria-labelを使用する

ボタンにspan要素がない場合、それを置き換える方法が必要です。1つの方法は、button要素またはsvg要素のいずれかでaria-label属性を使用することです。

アイコン付きボタンをさらに掘り下げたい場合は、Sara Soueidanの記事をご覧ください。

複数行のボタン

場合によっては、複数行のテキストを含むボタンが必要になることがあります。

複数行のボタン

複数行のボタン

このような複数行のボタンをどうやってアクセシブルに実装すればよいでしょうか? その方法を解説します。

スクリーンリーダーはこのボタンを「Subscribe to our newsletter 240K+ subscribers(ニュースレターに登録24万人以上の購読者)」と読み上げます。ユーザーはこの複数行のテキストの間に切れ目がないので混乱するかもしれません。
Chromeのアクセシビリティインスペクターのスクリーンショットをご覧ください。

Chromeのアクセシビリティインスペクター

Chromeのアクセシビリティインスペクターによる表示

この混乱を避けるために、2番目のテキストをスクリーンリーダーから隠したいと思います。そのためには、span要素でaria-hidden属性を使用します。

何らかの理由でHTMLを変更できない場合は、疑似要素で2番目のテキストを実装する方法もあります。この実装もスクリーンリーダーは2番目のテキストを読みません。

アンカーリンクとしてのa要素とbutton要素

アンカーリンクとしてa要素またはbutton要素を使用する場合、まず最初にその2つの要素を明確に区別しましょう。

アンカーリンクとは

Webサイトの別のページにリダイレクトする、または同じページのセクションにリンクするハイパーリンクを表します。:hover, :focus, :active, :visitedの4つの疑似クラスを使用できます。アクセシビリティの観点からみると、キーボードのEnterキーでリンクにアクセスでき、スクリーンリーダーはリンクであることを通知します。

button要素

<button type="button">要素は、JavaScriptで指定するまでリンクしません。:hover, :focus, :activeの3つの疑似クラスを使用できます。アクセシビリティの観点からみると、キーボードのEnterキーまたはSpaceキーでボタンにアクセスでき、スクリーンリーダーはボタンであることを通知します。

デザインの観点からみると、Webページに同じスタイルのボタンが含まれていて、HTMLの観点からは異なることがあります。見た目が同じで機能が異なるボタンがあることを説明するために、.c-buttonのCSSはbutton要素またはa要素に対して機能するように作成する必要があります。

見た目は同じボタンなのに、機能が異なる

見た目は同じボタンなのに、機能が異なる

見た目が同じボタンがあることに注目してください。1つ目はアンカーリンク<a>要素で、2つ目はフォームの<button>要素です。つまり、ボタンがHTMLの<button>で実装されている必要はないということです。

.c-buttonには、text-decorationとtext-align: center;の2つの機能があります。デフォルトではリンクに下線がついていますが、既存のリセットで取り除きます。さらに、ボタンのコンテンツが<button>要素で実装されない場合に備えて、ボタンのコンテンツを中央に配置しておくことも重要です。

<button>はボタンのように見える必要はない

私はScott O’Haraによるアクセシブルなアコーディオンを実装したデモが気に入っています。マークアップは、下記のようになります。

アクセシブルなアコーディオン

アクセシブルなアコーディオン

JavaScriptが使用可能な場合、上記のマークアップは<button>を作成し、<h2>要素内に追加することで、展開・折り畳み機能に変換されます。

展開・折り畳み機能付きのアコーディオン

展開・折り畳み機能付きのアコーディオン

詳細コンテンツを展開・折り畳みするために、<button>を使用するのは正しい選択です。

ダウンロード用のボタン

さまざまな種類のドキュメントがあり、それらをダウンロード可能なリンクが必要な場合、何を使用しますか? アンカーリンクが便利です。

リンクにdownload属性を加えることで、クリックされたときにダウンロードをトリガーにできます。

ダウンロード用のボタン

ダウンロード用のボタン

ボタンのスタイルのリンクを作成して、セマンティックな方法でその役割を果たすことができます。

アウトラインボタンの最適な実装方法

ホバー時にアウトラインになるボタン

ホバー時にアウトラインになるボタン

上記のボタンはホバーすると、アウトラインのボタンになります。最適な実装方法はどうすればよいでしょうか?

まず最初にノーマル時のborderを透明にしておきます。こうすることで、ホバー時にボーダーが新しく追加されるのを防ぎます。こうしておかないと、ホバー時にボタンのサイズがボーダー分大きくなってしまいます。

あとは、:hoverにborderのカラーを定義します。変更される背景色に合わせて、テキストのカラーを変更するのを忘れないように。

グラデーションボタンの最適な実装方法

CSS Positioningを書いている時に、グラデーションのボタンが必要になりました。
下記のようなボタンです。

グラデーションのボタン

グラデーションのボタン

ボタンにはグラデーションとアウトラインの2種類があり、これを実装するにはベースのボタンにはボーダーを透明にする必要があります。ボタンのボーダーはアウトラインの時のみ表示されます。

しかし、おかしな問題が起こりました。下記を見てみてください。

グラデーションボタンで起こった問題

グラデーションボタンで起こった問題

透明なボーダーがある場合のグラデーションボタンの見た目については理解できたので、この問題の技術的な理由を説明します。

各要素にはpadding-boxという値を持つデフォルトのbackground-originがあります。ボーダーの境界内に収まるようにグラデーションのサイズを変更します。

何が起きているのかを確認するために、ボタンを大きくしてみました。その結果、グラデーションが繰り返されてしまうのが分かり、そのサイズはbutton要素のpaddingと同じでした。

button要素のpaddingと同じに

button要素のpaddingと同じに

この問題を解決するには、background-originの値をpadding-boxではなく、border-boxにする必要があります。

heightとpaddingのどちらがよいか

ボタンの高さに、明示的なheightの値を使用するか、上下のpaddingで高さを確保するか、どちらがよいでしょう?
アンカーリンクのa要素でボタンを実装した例で見てましょう。

heightを使った固定値の高さ

heightの値を使用してボタンの高さを固定値で定義してみます。

このコードでは、ボタン内のテキストは中央に配置されません。

heightで高さを定義したボタン

heightで高さを定義したボタン

テキストを中央に配置するには、paddingにするか、line-heightを追加します。ボタンの高さと等しいまたは近い値でline-heightを定義すると、テキストが中央に配置されます。

この方法の欠点は、

  • ボタンのテキストが常に中央に配置されるとは限りません。高さが異なるフォントに変更された場合、中央には配置されません。
  • font-sizeを変更すると、line-heightの値も調整する必要があります。
  • 使用するWebページが多言語の場合、line-heightは一貫して機能しない場合があります。
  • ボタンのテキストが複数行の場合、機能しません。

上下にpaddingを確保した高さ

padding-topとpadding-bottomに同じ値を追加することで、ボタンのコンテンツが中央に配置されることが期待されます。正しいでしょうか? 場合によります。

センタリングが得意なフォントとそうでないフォントがあるので、場合によってはpaddingの値を少し調整する必要があります。

padding-topとpadding-bottomに同じ値を追加したボタン

padding-topとpadding-bottomに同じ値を追加したボタン

paddingの上下の値を同じにすると、中央からずれているように見えます。この場合は、padding-topの値を少し減らします。

ボタンのテキストをspanで内包する

調べているうちに、Adobeのボタンが少しずれていることに気付きました。下記のスクリーンショットをご覧ください。

ボタンのテキストが少しずれている

ボタンのテキストが少しずれている

これらのボタンのコードを調べたところ、興味深いパターンに気がつきました。テキストは、ボタンに対して固定の高さを持つ<span>要素で内包されていました。

<button>要素の場合、<span>要素はデフォルトで中央に配置されます。高さを変更すると、テキストは自動的に中央揃えになり、paddingは必要ありません。
heightの値を変更すると、自動的に中央揃えになるのは下記をご覧ください。

heightの値を変更すると、自動的に中央揃えに

heightの値を変更すると、自動的に中央揃えに

ただし、ボタンのように見えるリンクがある場合は、<span>要素を中央に配置する必要があります。そうするためには、Flexboxが便利です。

FlexboxまたはGridコンテナ内のボタン

ボタンとFlexboxまたはGridとの関係は何なのだろうと思っているかもしれません。下記を見てみてください。

ヒーローセクションを実装しようとした際に、コンテンツを垂直方向に中央揃えにしたいとします。Flexboxを使用して下記のように記述しました。

私はこの結果に驚きました。

ボタンが大きくなった!

ボタンが大きくなった!

デフォルトでは各Flexアイテムは親の幅で引き伸ばされるので、ボタンも引き伸ばされます。この問題を解決するには、ボタンの配置をリセットする必要があります。

ボタンが元のサイズになった

ボタンが元のサイズになった

もう1つよくある問題は、ボタンがFlexコンテナに内包されており、flex-direction: row;がデフォルトである場合です。今度はボタンの高さが親の高さに引き伸ばされます。この問題を解決するには、ボタンにalign-selfプロパティを使用するか、Flexコンテナにalign-itemsを使用します。

サイトのキャプチャ

今度は高さが引き伸ばされてる!

ボタンの配置をラッパーの中心にすることで、問題は解決します。

サイトのキャプチャ

aaaaa

ボタンのサイズ指定にemを使用する

ボタンのサイズを適切に設定するには、単位にemを使用します。emは優れもので、よく機能します。emの使い方は要素のfont-sizeに使う時と同様です。

この場合、paddingは16*0.5=8pxになります。

実際にボタンを例に見てましょう。
ボタンに3つのサイズ(標準・中・大)があるとします。emをpadding, width/height, marginに使用すると、簡単にサイズが変更できる流動的なボタンを実装できます。

3つのサイズ(標準・中・大)のボタン

3つのサイズ(標準・中・大)のボタン

padding, width/height, marginに、emを使用してみます。

ボタンについては、フォントのサイズが異なるタイプごとにclassを作成する必要があります。

See the Pen
Styling Buttons - Em Unit
by Ahmad Shadeed (@shadeed)
on CodePen.

これについて私は、CSS-Tricksで記事を書きました。
参考: Building Resizeable Components with Relative CSS Units

ボタンのアニメーションとトランジション

ユーザーにより良いエクスペリエンスを提供するには、ボタンがホバーされたときにトランジションを追加することが重要です。最も簡単な方法は、背景色のトランジションです。
追加したトランジションのデモは、次のセクションをご覧ください。

最終的に完成したボタン

See the Pen
Styling Buttons - Final Button
by Ahmad Shadeed (@shadeed)
on CodePen.

終わりに

この記事のための作業と調査は、楽しいものでした。1年以上もかかりましたが、これを公開できて嬉しく思います。気に入ってもらえることを願っています!

コメントや提案があれば、@shadeed9までお願いします。

関連記事

sponsors

top of page

©2020 coliss