見出し画像

【React設計の極致】見た目を捨てて「機能」に徹する。Headless UI パターンで再利用性を限界まで高める方法


96日目に「Suspense」を学び、データの「待ち方」を洗練させました。完走まであと4日。本日97日目は、中級から上級への大きなステップである「Headless UI(ヘッドレスUI)パターン」 を学習します。

「機能は同じだけど、見た目だけ変えたい」という現場で必ず直面する課題を、Reactの設計力でエレガントに解決しましょう!

はじめに:「見た目」に縛られたコンポーネントの限界

「以前作ったタブコンポーネント、別のプロジェクトでも使いたいけど……デザインが全然違うから結局作り直しだ」

そんな経験はありませんか?

HTML(構造)とCSS(見た目)が密結合していると、再利用性はガクンと落ちます。そこで登場するのが、「ロジック(動き)だけを提供し、見た目は一切持たない」 という Headless UI の考え方です。


💡 1. Headless UI とは何か?

Headless UI は、モーダルの開閉、ドロップダウンの選択、タブの切り替えといった「複雑な挙動やアクセシビリティ(キーボード操作など)」だけを管理するコンポーネントやHookのことです。

  • メリット: デザインの自由度が100%。どんなCSSフレームワーク(Tailwind, CSS Modulesなど)とも組み合わせられます。

  • アクセシビリティ: 自分で実装すると大変な aria- 属性の管理などを勝手にやってくれます。


💡 2. 実践:カスタムHookによる Headless 化

例えば、シンプルな「トグル(開閉)」機能を Headless 化してみましょう。

💻 ロジック担当 (useToggle.ts)

export const useToggle = (initialState = false) => {
  const [isOpen, setIsOpen] = useState(initialState);
  const toggle = () => setIsOpen(prev => !prev);
  
  // 💡 見た目に関係ない「状態」と「操作」だけを返す
  return { isOpen, toggle, buttonProps: { onClick: toggle, "aria-expanded": isOpen } };
};

🎨 見た目担当 (MyComponent.tsx)

const MyComponent = () => {
  const { isOpen, buttonProps } = useToggle();

  return (
    <div>
      <button {...buttonProps} className="btn-primary">
        {isOpen ? "閉じる" : "開く"}
      </button>
      {isOpen && <nav className="menu">...</nav>}
    </div>
  );
};

💡 3. 有名な Headless ライブラリ

一からすべてを作るのは大変なので、プロの現場では以下のライブラリがよく使われます。

  1. Headless UI (by Tailwind Labs): Tailwind CSS と相性抜群。

  2. Radix UI: 高機能でアクセシビリティが非常に強固。

  3. TanStack Table: 非常に複雑な「表(Table)」のロジックだけを管理する最強ツール。


📝 97日目のまとめと課題

✅ 本日の習得ポイント

  • Headless UI: 「動き」と「見た目」を完全に分離する設計パターン。

  • アクセシビリティの担保: キーボード操作やスクリーンリーダー対応をロジック層で解決する。

  • 高い再利用性: どんなプロジェクトのデザインにも適合できる柔軟性。

📝 97日目の課題

  1. 「モーダル」や「アコーディオン」など、自分がよく作るコンポーネントを思い浮かべてみましょう。

  2. その「ロジック(開閉状態、関数の定義)」だけをカスタムHookに追い出せるか試してみてください。

  3. Radix UI のドキュメントを眺めて、「こんなに複雑な動きをロジックだけで提供してくれるのか!」と感動してみてください。

もっとReactを学びたい人はここから!

#エンジニアと繋がりたい #プログラミング学習 #React #100DaysOfCode #今日の積み上げ #フロントエンド設計 #デザインシステム

いいなと思ったら応援しよう!