お疲れ。ちょっといいか。
最近のコードレビューをしていて、フォーム周りのスタイリングで「あ、これまだ昔の癖を引きずってるな」って思う実装をよく見かけるんだ。具体的に言うと、`input[readonly]` や `.is-disabled` みたいなクラスをわざわざJavaScriptやCSSで付与してスタイリングしているケースね。
おいおい、ちょっと待てと。モダンなCSSには、ブラウザが勝手に状態を判定して切り替えてくれる強力な擬似クラスが標準装備されているんだよ。それが今回話す `:read-only` と `:read-write` だ。
今回は、この2つの擬似クラスの基本から、現場でめちゃくちゃ重宝する `contenteditable` との組み合わせ、そして「おっ」と言わせる実務的なスタイリングの極意まで、余すところなく伝授しよう。フォームUIの解像度が一段階上がるはずだから、しっかりついてきてくれ。
—
1. そもそも `:read-only` と `:read-write` とは何か?
一言で言えば、「要素の中身をユーザーが編集できるか否か」をブラウザ自身に判定させ、スタイリングするためのフラグだ。
これまでの現場では、読み取り専用のテキストボックスに対してこんなCSSを書くことが多かったはずだ。
/ 昔ながらの泥臭いセレクタ /
input[readonly],
input:disabled {
background-color: #eee;
color: #666;
}
ここで重要なのは、`:disabled` は「無効化されている(フォーカスも当たらない)」状態だが、`:read-only` は「値は存在し、フォーム送信もされるが、ユーザーが直接書き換えることはできない」状態を指すという点だ。
そして `:read-write` はその対義語。デフォルトの `` や `
ブラウザの裏側の話:なぜこの擬似クラスが優れているのか?
ブラウザはDOMツリーを構築する際、各要素の属性(`readonly`, `disabled` など)や、後述する `contenteditable` の有無を常に監視している。
開発者が「この要素は編集不可だ」と明示的にクラス名をつける必要はなく、ブラウザがネイティブに持っているステータスをCSS側から直接フックできるのがこの擬似クラスの最大の強みだ。つまり、HTMLの真実(Single Source of Truth)とCSSのスタイルが完全に同期する。余計なクラスの付け替えバグに悩まされることがなくなるってわけだ。
—
2. 実務での真骨頂:`contenteditable` との組み合わせ
ここからが本題だ。中級から一段上のシニアへステップアップするために覚えておいてほしいのが、「divやpタグに `contenteditable` を付与したリッチテキスト領域のスタイリング」での活用法だ。
例えば、Notionのようなインライン編集可能なUIを作るとしよう。要素が「編集モード(アクティブ)」のときと「閲覧モード」のときで、UIの枠線や背景をパッと切り替えたい。そんなとき、JavaScriptでクラスをトグルしていなかったか? それ、実はCSSだけでスマートに完結するんだ。
以下のコードを見てほしい。実務でそのままコピペして検証環境で試せるように組んでみた。
1. input要素の読み取り専用切り替え
下のテキストボックスは上が通常(編集可)、下がreadonly属性つきです。
2. contenteditable要素の動的切り替え
下のボックスは、JavaScriptで contenteditable 属性を切り替えることで、CSSが自動でスタイリングを追従する仕組みになっています。
このサンプルを動かしてみるとよく分かるが、JavaScript側は「属性の真偽値を切り替えているだけ」で、見た目の制御は100%CSS側の `:read-only` と `:read-write` が担っている。これがモダンなフロントエンドの設計美というやつだ。余計なクラス名の管理コストがごっそり削れる。
—
3. 現場でハマりがちな「罠」とベストプラクティス
さて、ここまで聞くと「じゃあ全部これに置き換えよう!」と思うかもしれないが、シニアとしていくつか実務での注意点を釘を刺しておこう。
罠1: `:disabled` との混同
先ほども少し触れたが、`:disabled` は「フォームの無効化(入力不可・フォーカス不可・値の送信不可)」だ。それに対して `:read-only` は「値の送信はするが、見た目の変更や書き換えをさせない」もの。
例えば、ユーザープロフィール画面で「メールアドレスは変更不可だけど、確認のためフォームとして送信させたい」ときは `readonly` + `:read-only` を使うべきで、ボタンなどを押せなくしたいときは `disabled` を使うべきだ。用途をきっちり切り分けよう。
罠2: 詳細度(Specificity)の罠
CSS設計(BEMなど)を導入しているプロジェクトでは、セレクタの詳細度に少し気を配る必要がある。
例えば、 `.form-input:read-only` のようにクラスと組み合わせる分には問題ないが、タグセレクタ単体でスタイルを当てようとすると、他のユーティリティクラス(例: `.bg-gray-100` など)に上書きされてしまうことがある。
現場のコードベースに組み込む際は、状態をフックする擬似クラスは、ベースのコンポーネントクラスと組み合わせて定義するのが安全だ。
/ 良い例:コンポーネントのスコープ内で状態をフックする /
.c-input {
background-color: #fff;
border: 1px solid #ccc;
}
.c-input:read-only {
background-color: #eee;
border-color: #ddd;
}
—
まとめ
`:read-only` や `:read-write` は、派手なアニメーションを実装するような花形の機能じゃない。だが、こうしたブラウザのネイティブ仕様を深く理解し、HTMLのセマンティクスとCSSを美しく連携させることこそが、保守性の高い、バグの出ないフロントエンドアーキテクチャを支える基盤になる。
次にフォームやインライン編集UIを実装するとき、「あ、これクラスで状態管理しなくていいんだっけ、擬似クラス使お」とふと思い出してもらえたら、僕としてもこの記事を書いた甲斐があったというものだ。
さて、次のチケットに取りかかろうか。良いコードを書こうぜ。

コメント