こんにちは。君、最近のCSS、ちゃんと追いついてるかい?
「UIコンポーネントの実装なんて、とりあえずクラス付与してJSで制御すればいいや」なんて考えているとしたら、それは少しもったいない。CSSの標準仕様は、僕らが思っている以上に賢くなっているんだよ。
今回は、実務の現場でフォーム周りを実装するときに、知っているだけでコードの量が半分になり、保守性が劇的に跳ね上がる隠し味―― `:read-only` 疑似クラスについて話をしよう。
「ただの読み取り専用でしょ? `:disabled` と何が違うの?」と思ったそこの君。
今日のこの記事を読み終わる頃には、君のフォーム設計の引き出しが確実に一つ深くなっているはずだ。さあ、いってみよう。
—
1. `:read-only` 疑似クラスとは何か?(標準仕様とブラウザの裏側)
まずは基本の定義からおさらいしておこう。
`:read-only` は、ユーザーが直接値を変更できない(読み取り専用の)フォーム要素を選択するための疑似クラスだ。対象となるのは主に `` や `
ここで、多くのエンジニアが混同しがちなのが `:disabled` との違いだ。
- `:disabled`: 要素が無効化されている状態。クリックもフォーカスもできず、フォームの送信(FormData)にも値が含まれない。
- `:read-only`: 要素は読取専用だが、フォーカスは当たるし、テキストの選択やコピーもできる。もちろん、フォーム送信時にも値はサーバーに飛ぶ。
ブラウザの裏側の話を少ししよう。
ブラウザはDOMを構築する際、要素の属性(`readonly` 属性がついているか、あるいはそもそも編集不可な性質を持つ要素か)を評価している。
CSSの `:read-only` は、そのDOMの「編集可能性(Read/Writeな状態)」という内部フラグを直接監視してスタイリングを適用する。
つまり、開発者がいちいち `.is-readonly` なんていう無駄なクラスをJavaScriptで付け外ししなくても、ブラウザがネイティブに「お、この入力欄は今触れないな」と判断してスタイルを当ててくれるというわけだ。最高にスマートだと思わないかい?
—
2. 実務で遭遇する「落とし穴」と、それを華麗に回避する知見
さて、ここからがシニアとしての腕の見せ所、実務の泥臭い話だ。
実は、`:read-only` を使う上で、CSSの仕様(Selectors Level 4)に起因する「ちょっとした罠」がある。
歴史的な経緯もあり、ブラウザによっては 「通常のエディタブルな要素(デフォルトで書き換え可能な `` など)」までもが、`:read-only` の対象として誤認されてしまう実装 が過去に存在した(あるいは現在も厳密なセレクタ結合をしないと意図しない挙動をすることがある)。
実務で安全に、かつ確実に「読み取り専用の要素」だけにスタイルを当てたい場合は、属性セレクタと組み合わせるのがプロの作法だ。
/ 悪い例:これだけだと意図しない要素まで巻き込むリスクがある /
input:read-only {
background: #eee;
}
/ 良い例:readonly属性を持つ、または明示的に読み取り専用化された要素に絞る /
input:read-only,
textarea:read-only {
/ スタイルをここに記述 /
}
さらに、UI/UXの観点からも注意が必要だ。
「読み取り専用です」という見た目にするために、うっかり `cursor: not-allowed;` を指定したくなる気持ちは分かる。しかし、思い出してほしい。`:read-only` の要素は「テキストを選択してコピーできるべき」なんだ。
クリックしてカーソルが出ない、テキストが選択できないとなると、ユーザーは激しいストレスを感じる。だからこそ、カーソルはデフォルト(あるいは `text`)を維持しつつ、背景色やボーダーで「入力できない(しかし情報は取得できる)」雰囲気を醸し出すのがベストプラクティスだ。
—
3. コピペで使える!洗練されたフォームスタイリングの実装例
百聞は一見にしかず。実務のプロダクション環境ですぐに使える、モダンでアクセシブルなフォームのサンプルコードを用意した。
HTMLとCSSをそのままエディタに貼り付けて、挙動を確認してみてほしい。
HTML
CSS
/ 全体のベース設定 /
.form-group {
margin-bottom: 1.5rem;
font-family: sans-serif;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
font-size: 0.875rem;
color: #333;
}
/ 通常のinputの基本スタイル /
input[type=”text”] {
width: 100%;
max-width: 400px;
padding: 0.75rem;
font-size: 1rem;
border: 1px solid #cbd5e1;
border-radius: 0.375rem;
background-color: #ffffff;
color: #1e293b;
transition: border-color 0.2s, box-shadow 0.2s;
}
/ フォーカス時の挙動 /
input[type=”text”]:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}
/
★ ここが今回のメインテーマ:read-only 疑似クラスの適用
編集はできないが、テキストの選択やコピーは可能というUXを担保するため、
カーソルは ‘text’(またはデフォルト)にし、視覚的な色味だけで読み取り専用を表現する。
/
input[type=”text”]:read-only {
background-color: #f1f5f9;
border-color: #e2e8f0;
color: #64748b;
cursor: text; / ユーザーが値をコピーできるようにあえてtextカーソルにする /
}
/
参考::disabled のスタイル(read-onlyとの違いを視覚的にも明確にする)
/
input[type=”text”]:disabled {
background-color: #e2e8f0;
border-color: #cbd5e1;
color: #94a3b8;
cursor: not-allowed; / 操作自体が完全に不可なのでnot-allowed /
}
—
4. チーフアーキテクトからのまとめ
どうだい? `:read-only` 疑似クラスの魅力が伝わったかな。
JavaScriptでわざわざ `element.readOnly` を監視してクラスをトグルする必要なんて、もうどこにもないんだ。ブラウザのネイティブな状態管理(HTMLの属性)とCSSを直接リンクさせることこそが、モダンでバグの少ない、そしてパフォーマンスの高いフロントエンドアーキテクチャの基本姿勢だよ。
次にフォームを実装するときは、JSでの無駄な状態管理を疑い、CSSの標準機能でスマートに解決できないかまず考えてみてほしい。君の書くコードが、より洗練されたものになることを期待しているよ。さて、次のタスクに取り掛かろうか!

コメント