CSSで、kbd要素をキーボードのキーのような見た目にする方法

​​CSS​HTML,Playwright,アクセシビリティ

<kbd> に枠線と角丸を付け、下の枠線だけを太くすると、キーボードのキーのような見た目になります。枠と余白を付けただけでは、<kbd> のある行だけ間が少し広がるので、<kbd> の line-height を 1 にして、上下の padding を小さめにします。Ctrl + T のような組み合わせは、<kbd> を入れ子にして書き、外側の <kbd> だけ見た目を消します。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS のページを Playwright で開き、getComputedStyle() と getBoundingClientRect() の値を記録しました。等幅の文字は、検証環境に入っていた DejaVu Sans Mono で表示されています。Firefox と Safari、スマートフォンのブラウザー、スクリーンリーダーでの読み上げは試していません。

2つの画面を縦に並べたもの。上の「kbd に CSS を書かないとき」では、Ctrl、S、T、Page Down が等幅の文字になっているだけで、枠はない。下の「キーの見た目にしたとき」では、Ctrl、S、T、Page Down が、薄い灰色の背景に角丸の枠が付き、下の枠線だけ太いキーの形になっている。「Ctrl + T」の「+」は枠の外にある
同じ HTML に、CSS を書かないときと、この記事の CSS をあてたときの比較(幅 390px の画面)

HTML は、押すキーを kbd で囲む

HTML の仕様では、<kbd> はユーザーの入力(たいていはキーボードからの入力)を表す要素です。<kbd> の中にさらに <kbd> を入れると、内側の <kbd> は、実際のキーなど、1つの入力の単位を表します。

検証に使った HTML のうち、<kbd> を含む段落は次のとおりです。

html
<p>保存するには、<kbd>Ctrl</kbd> + <kbd>S</kbd> を押します。</p>
<p>新しいタブは <kbd><kbd>Ctrl</kbd> + <kbd>T</kbd></kbd> で開きます。</p>
<p>ページを1画面ずつ下へ送るには <kbd>Page Down</kbd> を押します。</p>

2行目は、外側の <kbd> が「Ctrl + T」という入力のまとまりを表し、内側の <kbd> が1つずつのキーを表します。MDN の <kbd> のページには、外側の <kbd> を省いて <kbd>Ctrl</kbd>+<kbd>N</kbd> と書いても有効だと書かれています。1行目は、外側を省いた書き方です。

<kbd> に CSS を書かないときは、文字が等幅になるだけで、枠は付きません。HTML の仕様で、ブラウザーの既定のスタイルを定めた節では、<kbd> は font-family: monospace です。

キーの見た目にする CSS

CSS は次のとおりです。

css
kbd {
  display: inline-block;
  padding: 0.15em 0.45em;
  border: 1px solid #aab4ba;
  border-bottom-width: 3px;
  border-radius: 5px;
  background: #f7f9fa;
  color: #1f2a30;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.85em;
  line-height: 1;
  white-space: nowrap;
}

kbd:has(> kbd) {
  display: inline;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  white-space: normal;
}

キーの形を作っているのは、border と border-bottom-width です。枠線を 1px にして、下だけを 3px にすると、キーの手前の側面が見えているような形になります。border-radius で角を丸め、background に薄い灰色を付けています。

文字は本文より少し小さい 0.85em にしました。本文が 16px のとき、キーの文字は 13.6px になります。font-family の先頭の ui-monospace は、MDN の font-family のページによると、ユーザーインターフェイスの既定の等幅フォントです。Menlo と Consolas が入っていない環境のために、最後に monospace を置いています。

2つ目のルールは、入れ子の外側の <kbd> から、キーの見た目を消すためのものです。理由は後の節で説明します。

行の間が広がらないように line-height を 1 にする

本文の line-height が 1.8、文字が 16px のページで、<kbd> を含む1行の段落の高さを測りました。<kbd> のない段落は 28.8px です。

<kbd> の CSS 段落の高さ キーの高さ
CSS を書かない 30.8px 19px
上の CSS の line-height を inherit にする(本文の 1.8 を受け継ぐ) 33.5px 32.53px
上の CSS の line-height を 1.4 にする 31.06px 27.09px
上の CSS の上下の padding を 0.2em にする 29.31px 23.03px
上の CSS のまま(line-height: 1、上下の padding 0.15em) 28.8px 21.66px

CSS を書かないときでも、<kbd> のある行は 2px 高くなりました。このとき、本文は Noto Sans CJK JP、<kbd> の中は DejaVu Sans Mono で表示されていました。

display: inline-block にした <kbd> では、キーの高さが大きいほど、段落も高くなりました。本文の line-height を受け継ぐと、キーが 32.53px になり、行が 4.7px 広がりました。line-height: 1 にして、上下の padding を 0.15em にすると、段落の高さは <kbd> がないときと同じ 28.8px になりました。

キーの入った行だけ間が広がると、段落の行の間がそろわなくなります。本文の文字の大きさや line-height を変えるときは、キーの上下の padding と枠線の太さもあわせて見直します。

入れ子の kbd は、外側の見た目を消す

kbd:has(> kbd) は、子に <kbd> を持つ <kbd>、つまり入れ子の外側だけに当てはまります。MDN の :has() のページにあるとおり、:has() は、引数の相対セレクターに一致する要素がある場合に、その要素を表す擬似クラスです。

このルールを消して、<kbd><kbd>Ctrl</kbd> + <kbd>T</kbd></kbd> を表示すると、次のようになりました。

このルールがないとき このルールがあるとき
外側の <kbd> キーの形の枠が付き、文字は 13.6px 枠がなく、文字は本文と同じ 16px
内側の <kbd> 文字が 11.56px 文字が 13.6px
段落の高さ 33.31px 28.8px

ルールがないと、外側の <kbd> も inline-block になり、内側のキーをまとめて囲む枠が付きます。内側の文字は、0.85em が2回かかって 16px × 0.85 × 0.85 = 11.56px まで小さくなりました。行も 4.5px 広がりました。

外側の <kbd> で font: inherit を指定すると、外側の文字は本文と同じ大きさとフォントに戻ります。内側の <kbd> は、そこから 0.85em で 13.6px になり、外側を省いて書いた <kbd>Ctrl</kbd> と同じ大きさになりました。

キーの途中で折り返さないようにする

Page Down のように、キーの名前に空白が入るときは、行の終わりでキーが2つに分かれることがあります。幅 320px の画面で試しました。

2つの画面を縦に並べたもの。上の「display: inline で、nowrap なし」では、Page Down のキーが行の終わりで「Page」と「Down」に分かれ、枠も2行にまたがって切れている。下の「inline-block と white-space: nowrap」では、「Page Down」のキーが分かれずに、まるごと次の行の先頭に置かれている
幅 320px の画面で、行の終わりにキーが来たときの表示

display: inline にして white-space: nowrap を外すと、Page Down のキーは2行に分かれ、枠も行の終わりと次の行の先頭で切れました。display: inline-block にすると、white-space: nowrap がなくても、キーは分かれませんでした。枠は1つのままで、高さも1行分の 21.66px でした。

この検証では、white-space: nowrap があるときとないときで違いが出たのは、キーの幅より狭い場所に置いたときだけでした。幅 60px の箱の中に置くと、white-space: normal のキーは枠の中で2行に折り返し、高さが 35.25px になりました。white-space: nowrap のキーは1行のままで、幅が 87.89px になり、箱からはみ出しました。MDN の text-wrap-mode のページには、nowrap の文字列は折り返されず、要素からはみ出すと書かれています。

アクセシビリティツリーでは、kbd はただの文章になる

Playwright でアクセシビリティツリーを出すと、<kbd> を入れ子にした段落は、ただの文章として出てきました。

text
- paragraph: 新しいタブは Ctrl + T で開きます。

MDN の <kbd> のページでも、<kbd> の暗黙の ARIA ロールは「対応するロールなし」です。この記事の HTML では「+」を <kbd> の外に文字で書いているので、アクセシビリティツリーにも「Ctrl + T」とそのまま出ます。

ページに font-size を書かないときの文字の大きさ

ページのどこにも font-size を書かず、<kbd> にも CSS を書かないページでは、本文が 16px なのに、<kbd> の文字は 13px になりました。<kbd> に font-family: monospace と font-size: 0.85em を書くと、13px のさらに 0.85 倍の 11.05px になりました。

font-family を ui-monospace, Menlo, Consolas, monospace のように、フォント名を並べた書き方にすると、<kbd> の文字は本文と同じ 16px になりました。この記事の CSS をあてたときは、本文の 0.85 倍の 13.6px です。font-family: monospace だけを書いたときに、キーの文字が思ったより小さくなったら、font-family の書き方を確かめてください。

​Posted by 86