tamagui
WebとネイティブアプリをまたいでUIを構築できるユニバーサルReactフレームワーク「Tamagui」に関するスキルです。`styled()`によるスタイル付きコンポーネントの作成、デザイントークンやテーマの設定、`XStack`/`YStack`などのUIコンポーネントの利用、アニメーションの実装など、Tamaguiを用いたクロスプラットフォーム開発全般で活用できます。`@tamagui/*`のimportや`useTheme`、`createStyledContext`、`variants`などをコードで使用する際にトリガーされます。
description の原文を見る
| Universal React UI framework for web and native. Use when building cross-platform apps with Tamagui, creating styled components with `styled()`, configuring design tokens/themes, using Tamagui UI components, or working with animations. Triggers: "tamagui", "styled()", "$token", "XStack/YStack", "useTheme", "@tamagui/*" imports, "createStyledContext", "variants".
SKILL.md 本文
Tamagui スキル
最適化コンパイラを備えた、ウェブとネイティブ向けのユニバーサル React UI フレームワーク。
プロジェクト固有の設定を取得する
Tamagui コードを書く前に、プロジェクトの実際の設定を取得してください:
npx tamagui generate-prompt
このコマンドは tamagui-prompt.md を出力します。以下の項目が含まれます:
- デザイントークン (space, size, radius, color, zIndex)
- テーマ名と階層
- 利用可能なコンポーネント
- メディアクエリのブレークポイント
- ショートハンドプロパティ
- フォントファミリー
デフォルト値を推測するのではなく、トークン/テーマ/メディアクエリ名について必ずこのファイルを参照してください。
コアコンセプト
styled() 関数
既存のコンポーネントを拡張して新しいコンポーネントを作成します:
import { View, Text, styled } from '@tamagui/core'
const Card = styled(View, {
padding: '$4', // トークンは $ で使用
backgroundColor: '$background',
borderRadius: '$4',
variants: {
size: {
small: { padding: '$2' },
large: { padding: '$6' },
},
elevated: {
true: {
shadowColor: '$shadowColor',
shadowRadius: 10,
},
},
} as const, // 型推論に必須
defaultVariants: {
size: 'small',
},
})
// 使用方法
<Card size="large" elevated />
重要なルール:
- variants オブジェクトには必ず
as constを使用 - トークンは
$プレフィックスを使用:$4,$background,$color11 - プロップの順序が重要 - 後のプロップが前のプロップをオーバーライド
- 後で定義されたバリアントが前に定義されたバリアントをオーバーライド
Stack コンポーネント
import { XStack, YStack, ZStack } from 'tamagui'
// XStack = flexDirection: 'row'
// YStack = flexDirection: 'column'
// ZStack = position: 'relative' with absolute children
<YStack gap="$4" padding="$4">
<XStack justifyContent="space-between" alignItems="center">
<Text>Label</Text>
<Button>Action</Button>
</XStack>
</YStack>
テーマ
テーマは階層的にネストして組み合わせます:
import { Theme } from 'tamagui'
// ベーステーマ
<Theme name="dark">
{/* サブテーマ */}
<Theme name="blue">
{/* dark_blue テーマを使用 */}
<Button>Blue button on dark</Button>
</Theme>
</Theme>
// テーマ値にアクセス
const theme = useTheme()
console.log(theme.background.val) // 実際のカラー値
console.log(theme.color11.val) // 高コントラストテキスト
12段階のカラースケール規約:
$color1-4: 背景 (微妙から強調)$color5-6: ボーダー、セパレータ$color7-8: ホバー/アクティブ状態$color9-10: 単色背景$color11-12: テキスト (低から高コントラスト)
レスポンシブスタイル
メディアクエリプロップを使用します (tamagui-prompt.md で実際のブレークポイント名を確認):
<YStack
padding="$4"
$gtSm={{ padding: '$6' }} // 設定ファイルで実際の名前を確認
$gtMd={{ padding: '$8' }}
flexDirection="column"
$gtLg={{ flexDirection: 'row' }}
/>
// またはフックで
const media = useMedia()
if (media.gtMd) {
// 中サイズ以上の画面で描画
}
アニメーション
import { AnimatePresence } from 'tamagui'
<AnimatePresence>
{show && (
<YStack
key="modal" // 終了アニメーションに必須のキー
animation="quick"
enterStyle={{ opacity: 0, y: -20 }}
exitStyle={{ opacity: 0, y: 20 }}
opacity={1}
y={0}
/>
)}
</AnimatePresence>
アニメーションドライバ:
@tamagui/animations-css- ウェブのみ、CSS トランジション@tamagui/animations-react-native- ネイティブ Animated API@tamagui/animations-reanimated- ネイティブの最高のパフォーマンス@tamagui/animations-motion- スプリング物理演算
CSS ドライバはイージング文字列を使用し、他のドライバはスプリング物理演算をサポートします。
複合コンポーネント
状態を共有するコンポーネントには createStyledContext を使用:
import { createStyledContext, styled, View, Text } from '@tamagui/core'
import { withStaticProperties } from '@tamagui/helpers'
const CardContext = createStyledContext({ size: 'medium' as 'small' | 'medium' | 'large' })
const CardFrame = styled(View, {
context: CardContext,
padding: '$4',
backgroundColor: '$background',
variants: {
size: {
small: { padding: '$2' },
medium: { padding: '$4' },
large: { padding: '$6' },
},
} as const,
})
const CardTitle = styled(Text, {
context: CardContext, // 親から size を継承
fontWeight: 'bold',
variants: {
size: {
small: { fontSize: '$4' },
medium: { fontSize: '$5' },
large: { fontSize: '$6' },
},
} as const,
})
export const Card = withStaticProperties(CardFrame, {
Title: CardTitle,
})
// 使用方法 - size が子にカスケード
<Card size="large">
<Card.Title>Large Title</Card.Title>
</Card>
よくあるパターン
Adapt を使った Dialog (モバイルでは Sheet)
import { Dialog, Sheet, Adapt, Button } from 'tamagui'
<Dialog>
<Dialog.Trigger asChild>
<Button>Open</Button>
</Dialog.Trigger>
<Adapt when="sm" platform="touch">
<Sheet modal dismissOnSnapToBottom>
<Sheet.Frame padding="$4">
<Adapt.Contents />
</Sheet.Frame>
<Sheet.Overlay />
</Sheet>
</Adapt>
<Dialog.Portal>
<Dialog.Overlay
key="overlay"
animation="quick"
opacity={0.5}
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
/>
<Dialog.Content
key="content"
animation="quick"
enterStyle={{ opacity: 0, scale: 0.95 }}
exitStyle={{ opacity: 0, scale: 0.95 }}
>
<Dialog.Title>Title</Dialog.Title>
<Dialog.Description>Description</Dialog.Description>
<Dialog.Close asChild>
<Button>Close</Button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
Input/Label を使ったフォーム
import { Input, Label, YStack, XStack, Button } from 'tamagui'
<YStack gap="$4" padding="$4">
<YStack gap="$2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
placeholder="email@example.com"
autoCapitalize="none"
keyboardType="email-address"
/>
</YStack>
<XStack gap="$2" justifyContent="flex-end">
<Button variant="outlined">Cancel</Button>
<Button theme="blue">Submit</Button>
</XStack>
</YStack>
アンチパターン
❌ トークンの代わりにハードコードされた値
// 悪い例
<View padding={16} backgroundColor="#fff" />
// 良い例 - デザイントークンを使用
<View padding="$4" backgroundColor="$background" />
❌ variants に as const がない
// 悪い例 - TypeScript がバリアント型を推論できない
variants: {
size: { small: {...}, large: {...} }
}
// 良い例
variants: {
size: { small: {...}, large: {...} }
} as const
❌ styled() 内でのプラットフォーム検出
// 悪い例 - コンパイラで抽出されない
const Box = styled(View, {
padding: Platform.OS === 'web' ? 10 : 20,
})
// 良い例 - プラットフォームモディファイアを使用
const Box = styled(View, {
padding: 20,
'$platform-web': { padding: 10 },
})
❌ AnimatePresence なしの exitStyle
// 悪い例 - 終了アニメーションが動作しない
{show && <View exitStyle={{ opacity: 0 }} />}
// 良い例
<AnimatePresence>
{show && <View key="box" exitStyle={{ opacity: 0 }} />}
</AnimatePresence>
❌ 抽出を防ぐ動的な値
// 悪い例 - ランタイム変数がコンパイラ抽出を防ぐ
const dynamicPadding = isPremium ? '$6' : '$4'
<View padding={dynamicPadding} />
// 良い例 - インライン三項演算子は抽出可能
<View padding={isPremium ? '$6' : '$4'} />
❌ 間違ったメディアクエリ順序
// 悪い例 - ベース値がレスポンシブをオーバーライド
<View $gtMd={{ padding: '$8' }} padding="$4" />
// 良い例 - ベースが最初、その次にレスポンシブがオーバーライド
<View padding="$4" $gtMd={{ padding: '$8' }} />
❌ CSS ドライバでのスプリングアニメーション
// 悪い例 - CSS ドライバはスプリング物理演算をサポートしない
import { createAnimations } from '@tamagui/animations-css'
const anims = createAnimations({
bouncy: { type: 'spring', damping: 10 } // 動作しない
})
// CSS ドライバ向けの良い例 - イージング文字列を使用
const anims = createAnimations({
bouncy: 'cubic-bezier(0.68, -0.55, 0.265, 1.55) 300ms'
})
コンパイラの最適化
Tamagui コンパイラはビルド時に静的スタイルを CSS に抽出します。スタイルが抽出されるには:
- トークンを使用 -
$4は抽出される、16は抽出されない場合がある - インライン三項演算子 -
padding={x ? '$4' : '$2'}は抽出される - ランタイム変数を避ける - 計算された値は抽出されない
- バリアントを使用 - 条件付きプロップより優れている
抽出が機能しているかを確認:
- 開発モードで
data-tamagui属性を探す - コンパイラが有効になっているとバンドルサイズが小さくなる
- スタイルは CSS クラスとして表示される、インラインではない
TypeScript
import { GetProps, styled, View } from '@tamagui/core'
const MyComponent = styled(View, {
variants: {
size: { small: {}, large: {} }
} as const,
})
// プロップ型を抽出
type MyComponentProps = GetProps<typeof MyComponent>
// カスタムプロップで拡張
interface ExtendedProps extends MyComponentProps {
onCustomEvent?: () => void
}
クイックリファレンス
| パターン | 例 |
|---|---|
| トークン | padding="$4" |
| テーマ値 | backgroundColor="$background" |
| カラースケール | color="$color11" (高コントラストテキスト) |
| レスポンシブ | $gtSm={{ padding: '$6' }} |
| バリアント | <Button size="large" variant="outlined" /> |
| アニメーション | animation="quick" enterStyle={{ opacity: 0 }} |
| テーマ切り替え | <Theme name="dark"><Theme name="blue"> |
| 複合 | <Card><Card.Title> with createStyledContext |
リソース
- ドキュメント: https://tamagui.dev
- GitHub: https://github.com/tamagui/tamagui
- Discord: https://discord.gg/tamagui
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- tamagui
- リポジトリ
- tamagui/tamagui
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/tamagui/tamagui / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。