mcpField
R
radix-ui

radix-ui

github.com/dralgorhythmデザイン・UI12856 回インストール14 週間前に更新
リポジトリ

radix-ui について

ReactのWeb開発向けの、スタイルを持たないアクセシブルなUIプリミティブです。キーボード操作、フォーカス管理、ARIA属性を備え、DialogやSelectなどの構成・スタイル設定・アクセシビリティの実践方法を扱います。

主な機能

  1. 01スタイルを持たないReact UIプリミティブ
  2. 02キーボード操作とフォーカス管理を内蔵
  3. 03ARIA属性を自動付与
  4. 04Dialog、Select、Tooltipを解説
  5. 05Tailwindのスタイル設定とテスト方法

活用シーン

  1. 01Webアプリのダイアログ構築
  2. 02キーボード操作可能なドロップダウンの実装
  3. 03インタラクティブなUIのスクリーンリーダー対応確認

MCP 対応

READMEでは、完全なAPIリファレンス、コンポーネントのプロパティ、例を調べるため、Context7 MCPの`resolve-library-id`と`query-docs`を使うよう案内しています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dralgorhythm/claude-agentic-framework --skill radix-ui

インストール後、Claude Code で/radix-uiを入力して読み込まれたか確認します。

詳細

Radix UI

プラットフォーム: Web のみです。モバイルのモーダル/シートについては、expo-sdk と react-native-patterns のスキルを参照してください。

API リファレンス、すべてのコンポーネントのプロパティ、追加の例については、Context7 MCP(resolve-library-id の後に query-docs)を使用してください。

概要

キーボードナビゲーション、フォーカス管理、ARIA 属性を標準で備えた、スタイル未設定のアクセシブルな React 用 UI プリミティブです。Tailwind CSS と Framer Motion と組み合わせて使うよう設計されています。

バージョン:最新(個別パッケージ)または radix-ui 統合パッケージ

インストール(個別パッケージ):

pnpm add @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-select @radix-ui/react-tooltip @radix-ui/react-tabs

インストール(統合パッケージ):

pnpm add radix-ui

統合パッケージの radix-ui にはすべてのプリミティブが含まれています。依存関係の管理を簡素化したい場合はこちらを使用してください。

ワークフロー

Dialog の追加:

  1. インストール:pnpm add @radix-ui/react-dialog
  2. Dialog のパーツをインポート:Root、Trigger、Portal、Overlay、Content
  3. 適切にスタックされるよう、Overlay と Content を Portal で囲む
  4. Tailwind と data-[state=] セレクターでスタイルを設定する
  5. キーボードナビゲーションをテストする(Esc キーで閉じる、Tab キーをモーダル内にとどめる)
  6. 必要に応じて Framer Motion のアニメーションを追加する

Select の追加:

  1. インストール:pnpm add @radix-ui/react-select
  2. Select のパーツをインポート:Root、Trigger、Portal、Content、Item
  3. 視覚的なフィードバックのため、Trigger に Icon と Value を追加する
  4. data-[state=open] で開閉状態のスタイルを設定する
  5. キーボード操作をテストする(矢印キー、Enter、タイプアヘッド)
  6. Portal の z-index が適切であることを確認する

ツールチップの追加:

  1. インストール:pnpm add @radix-ui/react-tooltip
  2. アプリを TooltipProvider で一度だけルートで囲む
  3. 各ツールチップで Trigger と Content を組み合わせる
  4. ホバー時の表示タイミングを設定するため、delayDuration を指定する
  5. スクリーンリーダーで読み上げられることを確認する

Dialog(モーダル)

import * as Dialog from '@radix-ui/react-dialog';
// OR: import { Dialog } from 'radix-ui';

function ModalExample() {
  const [open, setOpen] = useState(false);

  return (
    <Dialog.Root open={open} onOpenChange={setOpen}>
      <Dialog.Trigger asChild>
        <button className="px-4 py-2 bg-blue-600 text-white rounded">Open Dialog</button>
      </Dialog.Trigger>

      <Dialog.Portal>
        <Dialog.Overlay className="fixed inset-0 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" />

        <Dialog.Content className="fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] max-h-[85vh] w-[90vw] max-w-[500px] rounded-lg bg-white p-6 shadow-lg">
          {/* Title and Description are required for accessibility */}
          <Dialog.Title className="text-lg font-semibold mb-2">Dialog Title</Dialog.Title>
          <Dialog.Description className="text-sm text-gray-600 mb-4">
            Screen readers announce this description.
          </Dialog.Description>

          <div className="flex justify-end gap-2 mt-6">
            <Dialog.Close asChild>
              <button className="px-4 py-2 border rounded">Cancel</button>
            </Dialog.Close>
            <button className="px-4 py-2 bg-blue-600 text-white rounded">Save</button>
          </div>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}

Framer Motion のアニメーション:Portal に forceMount を指定し、AnimatePresence で囲みます。Overlay/Content に asChild を渡し、子要素として motion.div を使用します。

Select(ドロップダウン)

import * as Select from '@radix-ui/react-select';
import { ChevronDownIcon, CheckIcon } from '@radix-ui/react-icons';

function SelectExample() {
  return (
    <Select.Root defaultValue="apple">
      <Select.Trigger className="inline-flex items-center justify-between rounded px-4 py-2 text-sm bg-white border gap-2 data-[placeholder]:text-gray-400 min-w-[200px]">
        <Select.Value placeholder="Select a fruit..." />
        <Select.Icon><ChevronDownIcon /></Select.Icon>
      </Select.Trigger>

      <Select.Portal>
        <Select.Content className="overflow-hidden bg-white rounded-md shadow-lg border">
          <Select.Viewport className="p-1">
            <Select.Item value="apple" className="relative flex items-center px-8 py-2 rounded text-sm hover:bg-blue-50 outline-none cursor-pointer data-[disabled]:opacity-50">
              <Select.ItemIndicator className="absolute left-2"><CheckIcon /></Select.ItemIndicator>
              <Select.ItemText>Apple</Select.ItemText>
            </Select.Item>
            <Select.Item value="banana" className="relative flex items-center px-8 py-2 rounded text-sm hover:bg-blue-50 outline-none cursor-pointer">
              <Select.ItemIndicator className="absolute left-2"><CheckIcon /></Select.ItemIndicator>
              <Select.ItemText>Banana</Select.ItemText>
            </Select.Item>
          </Select.Viewport>
        </Select.Content>
      </Select.Portal>
    </Select.Root>
  );
}

グループ化した選択肢には Select.Group と Select.Label を、区切り線には Select.Separator を使用します。

Tailwind によるスタイリング

主なデータ属性セレクター
// Open/closed state
className="data-[state=open]:bg-blue-50 data-[state=closed]:bg-gray-50"

// Disabled
className="data-[disabled]:opacity-50 data-[disabled]:pointer-events-none"

// Checked (checkboxes, radio items)
className="data-[state=checked]:bg-blue-600"

// Side-based positioning
className="data-[side=top]:animate-slide-down data-[side=bottom]:animate-slide-up"
よく使われるレイアウトパターン
// Focus ring (required for keyboard UX)
className="outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"

// Centered modal
className="fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%]"

// Backdrop overlay
className="fixed inset-0 bg-black/50 backdrop-blur-sm"

アクセシビリティ

キーボードナビゲーション(自動処理):

  • Dialog:Esc キーで閉じる。Tab キーはモーダル内にとどまる
  • Select:矢印キーで移動し、Enter キーで選択。タイプアヘッド検索
  • DropdownMenu:矢印キーで移動し、Enter キーで選択
  • Tabs:矢印キーで移動し、Home/End キーで先頭/末尾へ移動
  • Tooltip:トリガーにフォーカスするとツールチップを表示

ARIA(自動付与):

  • Dialog:role="dialog"、aria-labelledby(Title)、aria-describedby(Description)
  • Select:role="combobox"、aria-expanded、aria-controls

スクリーンリーダーでの読み上げに必要なため、必ず Dialog.Title と Dialog.Description を指定してください。

カスタムのフォーカス管理:

<Dialog.Content
  onOpenAutoFocus={(e) => { e.preventDefault(); customRef.current?.focus(); }}
  onCloseAutoFocus={(e) => { e.preventDefault(); triggerRef.current?.focus(); }}
>

制御ありと制御なし

// Uncontrolled — component manages its own state
<Dialog.Root defaultOpen={false}>

// Controlled — parent manages state (preferred for complex UIs)
const [open, setOpen] = useState(false);
<Dialog.Root open={open} onOpenChange={setOpen}>

Portal の使用

z-index の競合、overflow: hidden によるクリッピング、CSS transform の問題を避けるため、オーバーレイやドロップダウンは必ず Portal に描画してください。

<Dialog.Portal>          {/* renders at document.body */}
  <Dialog.Overlay />
  <Dialog.Content />
</Dialog.Portal>

{/* Custom container */}
<Dialog.Portal container={customContainerRef.current}>

ベストプラクティス

  • asChild プロパティを使用すると、ラッパーの div なしでカスタム要素を組み合わせられます
  • オーバーレイやドロップダウンは必ず Portal を使用すると、z-index の問題を避けられます
  • すべての Dialog に Title と Description を指定する(アクセシビリティ要件)
  • 開閉状態のスタイリングには data-[state=] セレクターを使用する
  • 複雑な状態管理には制御ありのパターンを優先する
  • TooltipProvider はアプリのルートに 1 つだけ配置する。ツールチップごとには配置しないでください
  • インタラクティブなコンポーネントはすべてキーボードナビゲーションをテストする

アンチパターン

  • ❌ Dialog.Portal を忘れる(z-index の問題が発生します)
  • ❌ Dialog.Title または Dialog.Description がない(アクセシビリティ要件を満たせません)
  • ❌ カスタムトリガーに asChild を使わない(ラッパーの div が作成されます)
  • ❌ TooltipProvider を複数作成する(不要なオーバーヘッドが発生します)
  • ❌ onOpenChange/onValueChange を指定せずに制御ありのパターンを使う
  • ❌ 制御ありと制御なしのパターンを混在させる
  • ❌ focus:ring のスタイルを忘れる(キーボード操作の使い勝手が低下します)
  • ❌ キーボードナビゲーションでテストしない

フィードバックループ

アクセシビリティテスト:

# Test with keyboard only (no mouse)
# Tab through all interactive elements
# Esc should close Dialogs, Dropdowns, Selects
# Arrow keys should navigate menus and selects

スクリーンリーダーのテスト:

# macOS: VoiceOver (Cmd+F5)
# Verify Dialog.Title and Dialog.Description are announced
# Check for proper role attributes

ビジュアルリグレッション: すべての状態をテストします。閉じた状態と開いた状態、ホバー時とフォーカス時、選択状態と未選択状態、無効状態、さまざまなビューポートサイズを確認します。

Framer Motion: forceMount と AnimatePresence を使用します。アニメーション中もフォーカス管理が機能することをテストします。

コメント