mcpField
B
building-native-ui

building-native-ui

github.com/midudevデザイン・UI7.0k57 回インストール1 週間前に更新
リポジトリ

building-native-ui について

building-native-uiは、Expoアプリ開発者向けのUIガイドスキルです。ネイティブUI、ナビゲーション、スタイル、レスポンシブレイアウトを扱い、Expo Goの活用方法や各種リファレンスも示します。

主な機能

  1. 01Expo Routerのルートとナビゲーション規約
  2. 02ネイティブコントロール、フォーム、メディアの資料
  3. 03アニメーション、アイコン、グラデーションの指針
  4. 04レスポンシブレイアウトとセーフエリアの推奨事項
  5. 05Expo Goとカスタムビルドの選択ガイド

活用シーン

  1. 01Expoアプリのルート構成の設計
  2. 02iOS UI向けネイティブコントロールと操作の選定
  3. 03レイアウト、スタイル、ナビゲーション実装の確認

インストール

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

$npx skills add https://github.com/midudev/autoskills --skill building-native-ui

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
animations.md
controls.md
form-sheet.md
gradients.md
icons.md
media.md
route-structure.md
search.md
storage.md
tabs.md
toolbar-and-headers.md
visual-effects.md
webgpu-three.md
zoom-transitions.md

詳細

Expo UI ガイドライン

参考資料

必要に応じて、以下のリソースを参照してください。

references/
  animations.md          Reanimated: entering, exiting, layout, scroll-driven, gestures
  controls.md            Native iOS: Switch, Slider, SegmentedControl, DateTimePicker, Picker
  form-sheet.md          Form sheets in expo-router: configuration, footers and background interaction. 
  gradients.md           CSS gradients via experimental_backgroundImage (New Arch only)
  icons.md               SF Symbols via expo-image (sf: source), names, animations, weights
  media.md               Camera, audio, video, and file saving
  route-structure.md     Route conventions, dynamic routes, groups, folder organization
  search.md              Search bar with headers, useSearch hook, filtering patterns
  storage.md             SQLite, AsyncStorage, SecureStore
  tabs.md                NativeTabs, migration from JS tabs, iOS 26 features
  toolbar-and-headers.md Stack headers and toolbar buttons, menus, search (iOS only)
  visual-effects.md      Blur (expo-blur) and liquid glass (expo-glass-effect)
  webgpu-three.md        3D graphics, games, GPU visualizations with WebGPU and Three.js
  zoom-transitions.md    Apple Zoom: fluid zoom transitions with Link.AppleZoom (iOS 18+)

アプリの実行

重要:カスタムビルドを作成する前に、必ず最初に Expo Go を試してください。

ほとんどの Expo アプリは、カスタムのネイティブコードなしで Expo Go で動作します。npx expo run:ios または npx expo run:android を実行する前に、次の手順を行ってください。

  1. まず Expo Go を使用する:npx expo start を実行し、Expo Go で QR コードをスキャンします
  2. 機能が動作するか確認する:Expo Go でアプリを十分にテストします
  3. 必要な場合にのみカスタムビルドを作成する - 以下を参照してください
カスタムビルドが必要な場合

以下を使用する場合に限り、npx expo run:ios/android または eas build が必要です。

  • ローカル Expo モジュール(modules/ 内のカスタムネイティブコード)
  • Apple ターゲット(@bacons/apple-targets を介したウィジェット、App Clip、拡張機能)
  • Expo Go に含まれていないサードパーティ製ネイティブモジュール
  • app.json では表現できないカスタムネイティブ設定
Expo Go が使える場合

Expo Go は、幅広い機能を標準でサポートしています。

  • すべての expo-* パッケージ(カメラ、位置情報、通知など)
  • Expo Router によるナビゲーション
  • ほとんどの UI ライブラリ(reanimated、gesture handler など)
  • プッシュ通知、ディープリンクなど

迷った場合は、まず Expo Go を試してください。 カスタムビルドを作成すると複雑さが増し、反復作業が遅くなるうえ、Xcode/Android Studio のセットアップが必要になります。

コードスタイル

  • 文字列の閉じ忘れに注意してください。ネストされたバッククォートはエスケープし、引用符も必ず正しくエスケープしてください。
  • ファイルの先頭に必ず import 文を記述してください。
  • ファイル名には必ずケバブケースを使用してください(例:comment-card.tsx)。
  • ナビゲーションを移動または再構成する場合は、古いルートファイルを必ず削除してください。
  • ファイル名に特殊文字を使用しないでください。
  • tsconfig.json でパスエイリアスを設定し、リファクタリング時は相対インポートよりエイリアスを優先してください。

ルート

詳細なルートの規約については、./references/route-structure.md を参照してください。

  • ルートは app ディレクトリに配置します。
  • コンポーネント、型、ユーティリティを app ディレクトリに同居させないでください。これはアンチパターンです。
  • グループルート内でも構いませんので、アプリには必ず「/」に一致するルートを用意してください。

ライブラリの推奨事項

  • Picker、WebView、SafeAreaView、AsyncStorage など、React Native から削除されたモジュールは使用しないでください。
  • レガシーな expo-permissions は使用しないでください。
  • expo-av ではなく expo-audio を使用してください。
  • expo-av ではなく expo-video を使用してください。
  • SF Symbols には expo-symbols や @expo/vector-icons ではなく、source="sf:name" を指定した expo-image を使用してください。
  • react-native の SafeAreaView ではなく、react-native-safe-area-context を使用してください。
  • Platform.OS ではなく、process.env.EXPO_OS を使用してください。
  • React.useContext ではなく、React.use を使用してください。
  • 組み込みの img 要素ではなく、expo-image の Image コンポーネントを使用してください。
  • リキッドグラスの背景には expo-glass-effect を使用してください。

レスポンシブ対応

  • レスポンシブ対応のため、ルートコンポーネントは必ずスクロールビューでラップしてください。
  • <SafeAreaView> の代わりに <ScrollView contentInsetAdjustmentBehavior="automatic" /> を使用し、より適切なセーフエリアのインセットを適用してください。
  • FlatList と SectionList にも contentInsetAdjustmentBehavior="automatic" を適用してください。
  • Dimensions API ではなく Flexbox を使用してください。
  • 画面サイズの計測には、Dimensions.get() よりも必ず useWindowDimensions を優先してください。

動作

  • より心地よい体験を実現するため、iOS では条件に応じて expo-haptics を使用してください。
  • React Native の <Switch /> や @react-native-community/datetimepicker など、触覚フィードバックが組み込まれたビューを使用してください。
  • ルートが Stack に属する場合、最初の子要素にはほとんどの場合、contentInsetAdjustmentBehavior="automatic" を設定した ScrollView を指定してください。
  • ページに ScrollView を追加する場合は、ほとんどの場合、ルートコンポーネント内の最初のコンポーネントとして配置してください。
  • 検索バーを追加するには、Stack.Screen のオプションで headerSearchBarOptions を優先して使用してください。
  • コピーできるデータを含むテキストには、<Text selectable /> プロパティを使用してください。
  • 1.4M や 38k のように、大きな数値の書式設定を検討してください。
  • WebView または Expo DOM コンポーネント内を除き、'img' や 'div' などの組み込み要素は使用しないでください。

スタイル

Apple Human Interface Guidelines に従ってください。

一般的なスタイル設定ルール

  • margin や padding のスタイルより、flex gap を優先します
  • 可能な場合は、margin より padding を優先します
  • スタックヘッダー、タブ、または ScrollView/FlatList の contentInsetAdjustmentBehavior="automatic" を使い、常にセーフエリアを考慮します
  • 上部と下部の両方のセーフエリアインセットが考慮されていることを確認します
  • スタイルを再利用するほうが効率的な場合を除き、StyleSheet.create ではなくインラインスタイルを使用します
  • 状態が変化するときは、開始アニメーションと終了アニメーションを追加します
  • カプセル形状を作成する場合を除き、角丸には { borderCurve: 'continuous' } を使用します
  • ページ上にカスタムのテキスト要素を配置せず、必ずナビゲーションスタックのタイトルを使用します
  • ScrollView にパディングを設定するときは、ScrollView 自体ではなく contentContainerStyle の padding と gap を使用します(クリッピングを軽減できます)
  • CSS と Tailwind はサポートされていません。インラインスタイルを使用します

テキストのスタイル設定

  • 重要なデータやエラーメッセージを表示するすべての <Text/> 要素に、selectable プロパティを追加します
  • カウンターの位置を揃えるには、{ fontVariant: 'tabular-nums' } を使用します

シャドウ

CSS の boxShadow スタイルプロパティを使用します。React Native の従来の shadow や elevation のスタイルは絶対に使用しないでください。

<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />

'inset' シャドウがサポートされています。

ナビゲーション

リンク

ルート間のナビゲーションには、'expo-router' の <Link href="/path" /> を使用します。

import { Link } from 'expo-router';

// Basic link
<Link href="/path" />

// Wrapping custom components
<Link href="/path" asChild>
  <Pressable>...</Pressable>
</Link>

可能な場合は、iOS の規約に沿って <Link.Preview> を含めます。ナビゲーションを強化するため、コンテキストメニューとプレビューを積極的に追加します。

スタック

  • スタックの定義には、必ず _layout.tsx ファイルを使用します
  • ネイティブのナビゲーションスタックには、'expo-router/stack' の Stack を使用します
ページタイトル

Stack.Screen の options でページタイトルを設定します。

<Stack.Screen options={{ title: "Home" }} />

コンテキストメニュー

Link コンポーネントに長押しのコンテキストメニューを追加します。

import { Link } from "expo-router";

<Link href="/settings" asChild>
  <Link.Trigger>
    <Pressable>
      <Card />
    </Pressable>
  </Link.Trigger>
  <Link.Menu>
    <Link.MenuAction
      title="Share"
      icon="square.and.arrow.up"
      onPress={handleSharePress}
    />
    <Link.MenuAction
      title="Block"
      icon="nosign"
      destructive
      onPress={handleBlockPress}
    />
    <Link.Menu title="More" icon="ellipsis">
      <Link.MenuAction title="Copy" icon="doc.on.doc" onPress={() => {}} />
      <Link.MenuAction
        title="Delete"
        icon="trash"
        destructive
        onPress={() => {}}
      />
    </Link.Menu>
  </Link.Menu>
</Link>;

リンクプレビュー

ナビゲーションを強化するため、リンクプレビューを積極的に使用します。

<Link href="/settings">
  <Link.Trigger>
    <Pressable>
      <Card />
    </Pressable>
  </Link.Trigger>
  <Link.Preview />
</Link>

リンクプレビューはコンテキストメニューと併用できます。

モーダル

画面をモーダルとして表示します。

<Stack.Screen name="modal" options={{ presentation: "modal" }} />

カスタムのモーダルコンポーネントを作成するより、こちらを優先してください。

シート

画面を動的なフォームシートとして表示します。

<Stack.Screen
  name="sheet"
  options={{
    presentation: "formSheet",
    sheetGrabberVisible: true,
    sheetAllowedDetents: [0.5, 1.0],
    contentStyle: { backgroundColor: "transparent" },
  }}
/>
  • contentStyle: { backgroundColor: "transparent" } を使用すると、iOS 26 以降で背景がリキッドグラスになります。

一般的なルート構成

各タブ内にスタックを持つ、標準的なアプリのレイアウトです。

app/
  _layout.tsx — <NativeTabs />
  (index,search)/
    _layout.tsx — <Stack />
    index.tsx — Main list
    search.tsx — Search view
// app/_layout.tsx
import { NativeTabs, Icon, Label } from "expo-router/unstable-native-tabs";
import { Theme } from "../components/theme";

export default function Layout() {
  return (
    <Theme>
      <NativeTabs>
        <NativeTabs.Trigger name="(index)">
          <Icon sf="list.dash" />
          <Label>Items</Label>
        </NativeTabs.Trigger>
        <NativeTabs.Trigger name="(search)" role="search" />
      </NativeTabs>
    </Theme>
  );
}

両方のタブから共通画面へ遷移できるよう、共有のグループルートを作成します。

// app/(index,search)/_layout.tsx
import { Stack } from "expo-router/stack";
import { PlatformColor } from "react-native";

export default function Layout({ segment }) {
  const screen = segment.match(/\((.*)\)/)?.[1]!;
  const titles: Record<string, string> = { index: "Items", search: "Search" };

  return (
    <Stack
      screenOptions={{
        headerTransparent: true,
        headerShadowVisible: false,
        headerLargeTitleShadowVisible: false,
        headerLargeStyle: { backgroundColor: "transparent" },
        headerTitleStyle: { color: PlatformColor("label") },
        headerLargeTitle: true,
        headerBlurEffect: "none",
        headerBackButtonDisplayMode: "minimal",
      }}
    >
      <Stack.Screen name={screen} options={{ title: titles[screen] }} />
      <Stack.Screen name="i/[id]" options={{ headerLargeTitle: false }} />
    </Stack>
  );
}

コメント