フォームコントロールのスタイリング
問題
フォーム要素 — チェックボックス、ラジオボタン、レンジスライダー、テキストエリア — は、歴史的にウェブで最もスタイリングが難しい要素です。レンダリングはオペレーティングシステムに深く結びついており、ブラウザはCSSによる見た目の制御を制限しています。そのため、開発者はJavaScriptライブラリ、<div> 要素で構築されたカスタムトグルコンポーネント、または隠しinputと兄弟セレクタを使った複雑なCSSハックに頼ることになります。これらの回避策はバンドルを肥大化させ、ネイティブのアクセシビリティを壊し、本来のHTMLセマンティクスから逸脱してしまいます。
モダンCSSは、フォームコントロールを置き換えることなくブランディングやカスタマイズを行うネイティブプロパティを提供しています。
解決方法
4つのモダンCSSプロパティが、JavaScriptベースのフォームカスタマイズの大部分を置き換えます:
accent-color— 単一の宣言でネイティブのチェックボックス、ラジオボタン、レンジスライダー、プログレスバーにテーマを適用します。ブラウザがコントラストを自動的に処理します。appearance: none— フォームコントロールのOSネイティブレンダリングを除去し、CSSでゼロからスタイリングできる白紙の状態にします。field-sizing: content— テキストエリアやinputをコンテンツに合わせて自動サイズ調整させ、JavaScriptの自動リサイズスクリプトを不要にします。caret-color— inputやテキストエリアの点滅するテキストカーソルの色を変更し、ブランディングの繊細なアクセントになります。
基本原則
最初のツールとして accent-color を使いましょう — 単一プロパティ以外のカスタムCSSが不要で、完全なアクセシビリティを維持します。accent-color で表現できないデザインが必要な場合にのみ appearance: none を使いましょう。appearance: none を使用する場合は、Windows High Contrastモードでコントロールが表示されるよう、必ず @media (forced-colors: active) 内で復元してください。
たった1行のCSS — accent-color: hsl(262 80% 50%) — でコンテナ内のすべてのネイティブコントロールにテーマが適用されます。ブラウザがアクセントの背景に対してチェックマークやラジオドットの色を自動的にコントラスト調整します。
appearance: none 宣言はネイティブのチェックボックスレンダリングを除去し、CSSで再構築します:ボックスのボーダー、::before の clip-path ポリゴンでチェックマーク、チェック状態と非チェック状態のアニメーションに transform: scale() を使用します。@media (forced-colors: active) ブロックは appearance: auto を復元し、Windows High Contrastモードでチェックボックスが機能し続けるようにします。
カスタムラジオボタンもチェックボックスと同じパターンに従います:appearance: none でネイティブレンダリングを除去し、::before 擬似要素で内側のドットを描画し、transform: scale() で選択をスムーズにアニメーションします。円形の border-radius: 50% と小さな内側のドットにより、従来のラジオボタンの視覚言語が保持され、ユーザーはコントロールをすぐに認識できます。
field-sizing: content プロパティにより、JavaScriptの自動リサイズライブラリが不要になります。テキストエリアはユーザーが入力すると拡張し、コンテンツが削除されると縮小します。min-height と max-height で範囲を設定し、テキストエリアが1行に縮小したりページ全体を埋め尽くしたりしないようにしましょう。
ブラウザサポートの注意: field-sizing は Chrome 123+ と Edge 123+(2024年3月リリース)でサポートされています。Firefox と Safari はまだサポートしていません。プログレッシブエンハンスメントとして使用しましょう — 非対応ブラウザではテキストエリアがデフォルトの固定サイズにフォールバックします。
このフォームは4つのプロパティすべてを組み合わせて統一されたブランド体験を実現しています:accent-color でネイティブチェックボックスにテーマを適用し、caret-color で点滅カーソルをブランドのパープルに合わせ、box-shadow を使用したカスタムフォーカスリングでテキスト入力とテキストエリア全体に一貫したフォーカス処理を提供します。送信ボタンも同じカラーパレットに従い、適切な :hover、:focus-visible、:active 状態を備えています。
コード例
accent-color による手軽なブランディング
form {
accent-color: hsl(262 80% 50%);
}
この1行でフォーム内のすべてのチェックボックス、ラジオボタン、レンジスライダー、プログレスバーにテーマが適用されます。追加のセレクタは不要です。
ゼロからのカスタムチェックボックス
.checkbox {
appearance: none;
width: 1.25rem;
height: 1.25rem;
border: 2px solid hsl(215 20% 70%);
border-radius: 0.25rem;
display: grid;
place-content: center;
cursor: pointer;
}
.checkbox::before {
content: "";
width: 0.65rem;
height: 0.65rem;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
background: white;
transform: scale(0);
transition: transform 0.12s ease-in-out;
}
.checkbox:checked {
background: hsl(262 80% 50%);
border-color: hsl(262 80% 50%);
}
.checkbox:checked::before {
transform: scale(1);
}
/* Restore native appearance in Windows High Contrast mode */
@media (forced-colors: active) {
.checkbox {
appearance: auto;
}
}
自動サイジングテキストエリア
.auto-textarea {
field-sizing: content;
min-height: 3rem;
max-height: 20rem;
}
統一されたフォーム変数
.form {
--accent: hsl(262 80% 50%);
--accent-ring: hsl(262 80% 50% / 0.15);
accent-color: var(--accent);
caret-color: var(--accent);
}
.form input:focus,
.form textarea:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-ring);
outline: 2px solid transparent;
}
AIがよくやるミス
- ネイティブコントロールを
<div>要素で置き換える:<input>にappearance: noneを使う代わりに、<div>と<span>で偽のチェックボックスやラジオボタンを構築します。偽のコントロールはキーボードナビゲーション、フォーム送信、スクリーンリーダーのセマンティクスを欠いています。 @media (forced-colors: active)を忘れる:appearance: noneで構築されたカスタムチェックボックスやラジオボタンは、Windows High Contrastモードで見えなくなります。forced-colorsメディアクエリ内で常にappearance: autoを復元しましょう。- 自動拡張テキストエリアにJavaScriptを使用する:
field-sizing: contentがネイティブで処理できる(グレースフルデグラデーション付き)のに、リサイズオブザーバーやinputイベントリスナーを追加します。 accent-colorで十分な場合にすべてのフォームスタイルをオーバーライドする:ネイティブコントロールにブランドカラーを適用するだけで済むデザインに、数十行のカスタムチェックボックスCSSを書きます。- カスタムコントロールにフォーカスインジケーターを提供しない:
appearanceを除去しながら:focus-visibleスタイルを追加せず、キーボードユーザーに視覚的フォーカス状態がなくなります。 - CSSカスタムプロパティの代わりに色をハードコードする:変数の単一セットを定義する代わりに、セレクタ全体に色の値を散在させ、フォームテーマの柔軟性を失わせます。
使い分け
accent-color:ネイティブフォームコントロールにブランディングが必要で、デザインがカスタムの形状やレイアウトを要求しない場合に使います。これがデフォルトの正しい選択です。appearance: none:accent-colorでは表現できない完全にカスタムなチェックボックス、ラジオボタン、セレクトの外観がデザインで求められる場合に使います。必ずforced-colorsの復元とペアにしましょう。field-sizing: content:テキストエリアやテキストinputがコンテンツに合わせて拡大すべき場合に使います。min-height/max-heightの範囲を設定してプログレッシブエンハンスメントとして使用しましょう。caret-color:テキストinputやテキストエリアの点滅カーソルをブランドカラーに合わせるべき場合に使います。洗練されたデザインを示す小さなディテールです。