04. Spec KitでStitchのデザインをUIコンポーネントに実装する
画面骨格ができたら、次はアプリの「顔」となるUIコンポーネントを実装します。
リデザイン編(Phase03)では、Google Stitchを使って配色、余白、カードの角丸、影(シャドウ)、そして操作時のインタラクション仕様を DESIGN.md にまとめました。
この章では、その設計書をSpec Kit(AIエージェント)に正確に読み込ませ、チープなデフォルトUIを脱却した、洗練されたReact Nativeコンポーネント群へと変換していくプロセスを詳しく解説します。
この記事のサブコンテンツ
Section titled “この記事のサブコンテンツ”- デザインシステムをテーマ定数(theme.ts)として定義する
- Stitchのデザインから抽出したコアUIパーツの仕様
- React Native特有のスタイリング制約とプラットフォーム差の吸収
- Spec Kitの2周目:UIコンポーネントを仕様駆動で作る
- 生成されたUIコンポーネントの実装例(HabitCardと触覚フィードバック)
- 実機でのビジュアル確認とマイクロインタラクションの調整
- まとめと次のステップ
1. デザインシステムをテーマ定数(theme.ts)として定義する
Section titled “1. デザインシステムをテーマ定数(theme.ts)として定義する”AIエージェントが各コンポーネントで勝手なカラーコード(#123456など)を直書きするのを防ぐため、まずは DESIGN.md のトークンをまとめた constants/theme.ts を作らせます。
export const Colors = { primary: '#10B981', // エメラルドグリーン(アクション・完了状態) primaryLight: '#D1FAE5', // 完了カードの背景アクセント secondary: '#6366F1', // インディゴブルー(統計情報・ハイライト) background: '#F9FAFB', // 画面背景(オフホワイト) surface: '#FFFFFF', // カード背景(純白) textPrimary: '#111827', // メインテキスト textSecondary: '#6B7280',// サブテキスト border: '#E5E7EB', // 境界線};
export const Spacing = { xs: 4, sm: 8, md: 16, lg: 24, xl: 32,};
export const BorderRadius = { sm: 8, md: 12, lg: 16, full: 9999,};
export const Typography = { h1: { fontSize: 24, fontWeight: '700' as const, color: Colors.textPrimary }, h2: { fontSize: 18, fontWeight: '600' as const, color: Colors.textPrimary }, body: { fontSize: 14, fontWeight: '400' as const, color: Colors.textPrimary }, caption: { fontSize: 12, fontWeight: '400' as const, color: Colors.textSecondary },};ここで定義した値は、リデザイン編で作成した DESIGN.md のカラーシステムとタイポグラフィをそのまま写したものです。AIが勝手に別の色を使い始めたら、DESIGN.md と constants/theme.ts の両方を読み直させてください。
このようにテーマを一箇所に集約することで、後からのダークモード対応やブランドカラーの変更も容易になります。
2. Stitchのデザインから抽出したコアUIパーツの仕様
Section titled “2. Stitchのデザインから抽出したコアUIパーツの仕様”サンプルアプリ(HabitFlow)のホーム画面で必要な共通パーツは以下の3点です。
HabitCard(習慣カード):- 習慣名、継続日数バッジ(🔥 5日連続など)、完了チェックボタン
- タップ時にチェック状態が切り替わり、カード全体の見た目が変化する(完了時は緑のチェックと淡い背景)
ProgressBar(達成進捗バー):- 今日の達成率(例: 「3/5 完了 60%」)をグラフィカルに表示
FloatingAddButton(習慣追加ボタン):- 画面右下に浮かぶ丸いFAB(Floating Action Button)または固定の追加ボタン
3. React Native特有のスタイリング制約とプラットフォーム差の吸収
Section titled “3. React Native特有のスタイリング制約とプラットフォーム差の吸収”WebのCSSと異なり、React NativeのStyleSheetには注意すべき特徴があります。
- デフォルトが
flex-direction: column: Webのrowと逆なので、横並びにする際は明示的にflexDirection: 'row'を指定します。 - 影(シャドウ)のOS差:
- iOS:
shadowColor,shadowOffset,shadowOpacity,shadowRadiusで指定。 - Android:
elevation(数値)で指定。
- iOS:
- プラットフォーム分岐の活用:
Platform.select({ ios: { ... }, android: { ... } })を使ってAIにOSごとの最適値を吐かせます。
4. Spec Kitの2周目:UIコンポーネントを仕様駆動で作る
Section titled “4. Spec Kitの2周目:UIコンポーネントを仕様駆動で作る”ここからが Spec Kit サイクルの2周目です。前の記事と同じ順序(仕様 → 計画 → タスク → 実装)を、今度はUIコンポーネントに対して回します。
手順1:/speckit.specify で何を作るかを書く
Section titled “手順1:/speckit.specify で何を作るかを書く”/speckit.specify
ホーム画面で使う共通UIコンポーネントを用意する。
ルートの `DESIGN.md` および `constants/theme.ts` を読み込んでください。以下の共通UIコンポーネントを `components/` ディレクトリ配下に作成してください。
### 作成するコンポーネント1. `components/HabitCard.tsx` - props: `title: string`, `streakDays: number`, `isCompleted: boolean`, `onToggle: () => void` - チェックボタン(TouchableOpacity)を押すと `onToggle` を呼び出す。 - 完了状態のときは、カードの背景色やテキストスタイルを `DESIGN.md` の完了時スタイルに切り替える。 - タップ時に `expo-haptics` を使って軽い振動(ImpactFeedbackStyle.Light)を発生させること。2. `components/ProgressBar.tsx` - props: `total: number`, `completed: number` - アニメーションまたはスムーズに進捗バーを描画し、パーセンテージを表示する。3. `components/FloatingAddButton.tsx` - 画面右下に浮かぶ丸いFAB。`onPress` を受け取り、わずかなドロップシャドウで浮遊感を出す。4. `components/AddHabitModal.tsx` - 習慣の新規作成用のモーダルシート。TextInputと登録・キャンセルボタンを備える。
### 規約- スタイリングには React Native の `StyleSheet.create` を用い、色は `constants/theme.ts` の定数のみを使用すること。- iOSとAndroidの両方で美しいドロップシャドウ(elevationとshadowプロパティ)を付与すること。
### この機能でやらないこと- 習慣データの保存・読み込み(次の記事で実装する)- 画面への組み込み(コンポーネント単体で完結させる)手順2:/speckit.plan で技術的な方針を決める
Section titled “手順2:/speckit.plan で技術的な方針を決める”/speckit.plan
- 触覚フィードバックには `expo-haptics` を使う。未導入なら `npx expo install expo-haptics` をタスクに含めること。- 色・余白・角丸は必ず `constants/theme.ts` の定数を参照し、 カラーコードをコンポーネント内に直接書かないこと。- OS差のあるシャドウは `Platform.select` で iOS / Android を分岐させる。- 各コンポーネントは props だけで見た目が決まる純粋な表示部品とし、 AsyncStorage やグローバルな状態に依存しないこと。手順3:タスクに割って実装する
Section titled “手順3:タスクに割って実装する”/speckit.tasks/speckit.implementspecs/002-.../tasks.md にコンポーネントごとの作業が並び、上から順に実装されていきます。
5. 生成されたUIコンポーネントの実装例(HabitCardと触覚フィードバック)
Section titled “5. 生成されたUIコンポーネントの実装例(HabitCardと触覚フィードバック)”AIが生成した components/HabitCard.tsx のコード例です。タップした瞬間に心地よい振動が手元に伝わります。
import React from 'react';import { StyleSheet, Text, View, TouchableOpacity, Platform } from 'react-native';import { Ionicons } from '@expo/vector-icons';import * as Haptics from 'expo-haptics';import { Colors, Spacing, BorderRadius } from '../constants/theme';
interface HabitCardProps { title: string; streakDays: number; isCompleted: boolean; onToggle: () => void;}
export const HabitCard: React.FC<HabitCardProps> = ({ title, streakDays, isCompleted, onToggle,}) => { const handlePress = () => { // 触覚フィードバックを発火 if (Platform.OS !== 'web') { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); } onToggle(); };
return ( <View style={[styles.card, isCompleted && styles.cardCompleted]}> <TouchableOpacity style={[styles.checkbox, isCompleted && styles.checkboxCompleted]} onPress={handlePress} activeOpacity={0.7} accessibilityRole="checkbox" accessibilityLabel={title} accessibilityState={{ checked: isCompleted }} hitSlop={10} > {isCompleted && <Ionicons name="checkmark" size={18} color="#FFFFFF" />} </TouchableOpacity>
<View style={styles.content}> <Text style={[styles.title, isCompleted && styles.titleCompleted]}> {title} </Text> <Text style={styles.streak}>🔥 {streakDays}日連続</Text> </View> </View> );};
const styles = StyleSheet.create({ card: { flexDirection: 'row', alignItems: 'center', backgroundColor: Colors.surface, padding: Spacing.md, borderRadius: BorderRadius.md, marginBottom: Spacing.sm, borderWidth: 1, borderColor: Colors.border, ...Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.05, shadowRadius: 4, }, android: { elevation: 2, }, }), }, cardCompleted: { backgroundColor: Colors.primaryLight, borderColor: 'transparent', }, checkbox: { width: 28, height: 28, borderRadius: BorderRadius.sm, borderWidth: 2, borderColor: Colors.primary, alignItems: 'center', justifyContent: 'center', marginRight: Spacing.md, }, checkboxCompleted: { backgroundColor: Colors.primary, borderColor: Colors.primary, }, content: { flex: 1, }, title: { fontSize: 16, fontWeight: '600', color: Colors.textPrimary, }, titleCompleted: { color: Colors.textSecondary, textDecorationLine: 'line-through', }, streak: { fontSize: 12, color: Colors.textSecondary, marginTop: 2, },});6. 実機でのビジュアル確認とマイクロインタラクションの調整
Section titled “6. 実機でのビジュアル確認とマイクロインタラクションの調整”完成したコンポーネントを app/(tabs)/index.tsx に並べ、スマホ実機で確認します。
チューニングのプロンプト例
Section titled “チューニングのプロンプト例”実機で見て「少し余白が狭い」「チェックした時のアニメーションが欲しい」と感じた場合は、AIに直感的にフィードバックします。
「実機で確認したところ、カードの上下パディングが少し窮屈です。Spacing.mdからSpacing.lg相当に変更し、チェックボタンをタップした際にカード全体がふわっとフェードインするような心地よいトランジションを追加してください。」
このように、「実機で触る → 感覚的なフィードバックをAIに伝える → コードが即座にブラッシュアップされる」 というサイクルこそが、バイブコーディングの真骨頂です。
7. まとめと次のステップ
Section titled “7. まとめと次のステップ”Stitchで設計したビジュアルが、手元のスマートフォンの上で生き生きと動くReact Nativeコンポーネントになりました。
次の記事では、ローカルストレージ(AsyncStorage)によるデータの保存ロジックを結びつけ、アプリを完全に動くMVPとして完成させ、GitHubに保存する「05. 最初の動く版を作ってGitHubに保存する」に進みましょう。