世界を動かす技術を、日本語で。

Shadcnラジオボタンの過剰な複雑さ

概要

  • ShadcnRadix のReactコンポーネントを使ったラジオボタンの実装事例
  • ネイティブHTML要素カスタムUIライブラリ の比較
  • ARIA属性Tailwind CSS による複雑化の指摘
  • CSSだけでのシンプルなカスタマイズ 方法の紹介
  • Web開発の複雑化 とパフォーマンス低下への懸念

ShadcnとRadixによるラジオボタン実装の現状

  • 仕事で Webアプリのラジオボタン のデザイン更新を依頼された体験談
  • 伝統的な <input type="radio"> 要素のシンプルさの強調
  • 実際のコードベースでは ShadcnのReactコンポーネント (RadioGroup, RadioGroupItem)を使用
  • Shadcn は従来のUIフレームワークと異なり、npm installではなくコードを直接プロジェクトにコピーする運用
  • コード内で RadixLucide React など複数の外部ライブラリ、30以上のTailwindクラスを利用
  • SVGアイコン や複雑なクラス指定による肥大化と依存性の増加
    • 例:CircleIconのためだけに外部アイコンライブラリを導入

Radixの仕組みと複雑化の原因

  • ShadcnRadix Primitives をベースにスタイルを追加
  • Radix 自体はアクセシビリティやカスタマイズ性重視の「素の」UI部品群を提供
  • 実際には ボタン要素SVGARIA属性 でラジオボタンを再実装
  • ARIA属性 で「ボタンをラジオボタンとして解釈させる」手法
  • First Rule of ARIA (ネイティブ要素を優先)の原則に反する設計
  • <form>内でのみ **隠し<input type="radio">** を追加する特殊な挙動

ラジオボタンのスタイリングは本当に難しいのか

  • Radix がカスタム実装を選ぶ理由は「スタイリングの簡便化」と推測
  • 近年は appearance: none:checked::before などのCSSだけで十分なカスタマイズが可能
  • 例:CSSのみで丸いラジオボタン+選択時のドット表示を実現
    • 依存ライブラリ・JS・ARIA不要
  • Tailwindでも同様の実装が可能
  • Shadcnのコンポーネントも結局は大量のクラス指定が必要

シンプルなHTML+CSSの例

  • ネイティブの <input type="radio"> とシンプルなCSSで十分な表現力
  • 追加の依存や複雑なロジック不要
  • 例:
    input[type="radio"] {
      appearance: none;
      margin: 0;
      border: 1px solid black;
      background: white;
      border-radius: 50%;
      display: inline-grid;
      place-content: center;
    }
    input[type="radio"]::before {
      content: "";
      width: 0.75rem;
      height: 0.75rem;
      border-radius: 50%;
    }
    input[type="radio"]:checked::before {
      background: black;
    }
    

ライブラリ利用の是非とWeb開発の複雑化

  • コンポーネントライブラリ 利用の動機は「手軽さ」と「問題解決への集中」
  • ただし「できるだけ ネイティブ要素 を使う」ことの重要性を強調
  • 小さな最適化でも積み重なると 複雑化・パフォーマンス低下・バグ増加 の要因
  • JS依存 による初期表示遅延やファイルサイズ増加の懸念
  • 結論:「 ラジオボタンはシンプルでいい」「過剰な抽象化・ライブラリ依存は慎重に」

まとめ:本質を見失わないWeb開発

  • ラジオボタン のような基本UIは ブラウザの機能 を活用
  • 理解しやすさ・保守性・パフォーマンス を重視
  • 小さな選択の積み重ねが 全体の品質 に影響
  • シンプルなHTMLが持つ美しさと合理性

おまけ

  • 「Tiled Words」という無料の単語パズルゲームの紹介

Hackerたちの意見

なんか、こういうプロジェクトって、最初はいい意図で始まって、見た目も良くて、実装してそのまま放置されちゃうんだよね。で、ある日気づいたら、ラジオボタンのために200行以上のコードと7つのインポートがあって、もう全部を引っ張り出さないと戻れない状況になってる。これがコードの腐敗の始まりなんだよ。

shadcnのラジオボタンの実際の動きはこちら: https://ui.shadcn.com/docs/components/radio-group

このインタラクティビティ、確かにすごい効果を与えてるね。

これって、聞いたことのある10倍の開発者たちが開発したの?

プロのヒント:UIコントロールとラベルの間のスペースは、パディング(またはラベルのネストを使って)で作るべきだよ。そうすれば、全体がクリック可能になるから。[ x ] 長いラベル ꜛꜛꜛ ここにパディング、マージンや隙間じゃなくてね(コントロールとラベルの間をクリックしても何も起こらないから)。

Radixの元の作者たちに、なんでこうなってるのか聞いたのかな?

まさにその通り。OPは、こうなった理由を理解していないし、何千時間もこれに取り組んできた人が知らないことを知っているかもしれないってことを分かってないんだよ。

2020年には、ラジオボタンはすべての主流のブラウザで簡単にスタイルできなかったんだ。それが、いくつかのコンポーネントが過剰に設計される理由だよね。もちろん、すべてのブラウザが揃ったときに簡略化すべきだったけど、技術的負債は厄介なんだ。

普段はこの記事の意見に賛同するけど、ちょっと気になることがあるんだ。もし目標が、- デザイナーがfigmaファイルで送った通りにラジオボタンを実装すること(例えば、彼らがコメントしてるradixのデモみたいに: https://www.radix-ui.com/primitives/docs/components/radio-gr...) - すべてのブラウザで全く同じ見た目にすること だったとしたら、バニラCSSでどれくらい実現可能なの?彼らが示した例は黒白の円にレンダリングされてたけど、ほとんどのチームはそれを出荷しないよね。

でも、どこで線引きするの?ラジオボタンのちょっとした視覚的不一致を避けるために、どれくらいのキロバイトと将来のメンテナンス作業が必要なの?ネットワーク接続が悪い人たちを失う価値があるの?このアプローチをどこでも使ったら、ページの実際のコンテンツ(人々が求めてるものね)が影響を受けちゃう。思い出すのは、世界的に有名なビデオアーティスト、ナム・ジュン・パイクの言葉。「完璧になると、神は怒る」ってやつ。

ちょっとした修正だけでかなり近づけるよ。

input[type="radio"] {
  appearance: none;
  margin: 0;
  width: 25px;
  height: 25px;
  background: white;
  border-radius: 50%;
  display: inline-grid;
  place-content: center;
  box-shadow: 0 2px 10px color(display-p3 0 0 0/0.5);
  &::before {
    content: "";
    width: 11px;
    height: 11px;
    border-radius: 50%;
  }
  &:checked::before {
    background: color(display-p3 0.383 0.317 0.702);
  }
}

これがどう見えるか、コードペンのリンクを貼っとくね。自分でレンダリングしなくても見れるよ: https://codepen.io/erikaja/pen/RNRVMyB

  • すべてのブラウザで全く同じに見えるようにしろ > バニラCSSでどれくらい実現可能なの? おしゃれなフロントエンドフレームワークや20個のインポート、1万行のTypeScriptじゃ無理だよ。「すべてのブラウザで全く同じに見えるようにしろ」ってのは、ウェブが本来どう機能するべきかと根本的に矛盾してる。 このshadcnのクソみたいなのは、arachneやladybird、netsurf、links、dillo、netscape 3でどれくらいレンダリングされるの? 最新のChromeでユーザースタイルを適用した場合はどう? 「全く同じ」って言う時、デザインが黒と白だけを使ってるってことだよね?だって、モノクロモニターの人もいるかもしれないし。 でも、アンバーオンブラックも使うつもりだよね?だって、アンバースクリーンのモニターの人もいるかもしれないし。 点字端末で全く同じに見えるようにするつもりはどうなってるの? もしかして、俺がバカみたいに思えるかもしれないけど、2026年にはモノクロモニターなんて誰も使ってないよね?だからそれを無視して「全く同じ」の横にアスタリスクを付けるのが安全だよね(そしてe-inkが存在することも忘れちゃう)。 (2006年には、誰もが800x600以上のディスプレイを持っていると仮定するのが安全だったのと同じように、480×320の解像度の画面を使う人が現れることはないと思ってたよね) 色が異なるモニターの種類やブランドで全く同じに見えるようにするために、どんな対策を講じたの? それとも、「全く同じ」の横にもう一つアスタリスクを付けるべき? どれくらいのアスタリスクがあれば「全く同じ」じゃなくなるの? 「すべてのブラウザで全く同じに見えるようにする」ってのが目標の一つなら、間違ってるよ。デザイナーがそう言ったら、彼らも間違ってる。もしクライアントにそれを提供すると言ったら、君も間違ってる。
Hacker Newsで議論の続きを見る