feat(figgos): add NativeWind design system with semantic color tokens

CSS-variable-based design system (ManaDeck pattern) with light/dark mode,
semantic tokens (primary, secondary, accent, destructive, surface, muted),
and game-specific rarity colors (common, rare, epic, legendary).
Also adds FEATURES.md with game concept and data model.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Chr1st1anG 2026-02-10 21:15:03 +01:00
parent 1cffc6de81
commit 8836dfa6b1
10 changed files with 368 additions and 51 deletions

210
apps/figgos/FEATURES.md Normal file
View file

@ -0,0 +1,210 @@
# Figgos — Game Features
## Game Idea
Figgos ist ein AI-powered Collectible Figure Game. User erstellen personalisierte Action-Figuren im Toy-Packaging-Stil — realistische Spielzeugverpackungen mit Karton-Rueckwand, Plastik-Blister und Zubehoer. Die Figuren koennen alles sein: man selbst, Freunde, Fantasy-Charaktere, Promis — egal was. Jede Figur hat ein Rarity-Level, Artefakte/Items und eine Hintergrundgeschichte. Figuren koennen gegeneinander kaempfen oder miteinander gemercht werden um neue Figuren zu erzeugen.
---
## Core Features
### 1. Figure Generation
User erstellt eine neue Action Figure in Toy-Box-Verpackung.
**Inputs:**
- Foto/Gesicht (optional — fuer personalisierte Figuren)
- Name (Pflicht)
- Beruf / Rolle / Beschreibung (Pflicht)
- Accessoire-Items (optional, bis zu 4-6 Stueck)
- Accessoire-Bilder (optional — KI recreated diese exakt im Bild)
- Special Styling (optional — Farben, Themes, Hintergrund)
**AI Pipeline:**
1. Text-KI generiert Character-Info:
- Description + Lore
- 3 Artefakte/Items (je mit Name, Description, Lore)
- Style-Description fuer Bild-Generierung
2. Rarity wird gewuerfelt (oder vom User gewaehlt)
3. Bild-KI generiert die Figur in Toy-Box-Verpackung:
- Karton-Rueckwand mit Name + Titel
- Plastik-Blister mit Figur
- Items/Accessoires um die Figur arrangiert
- Professionelles Produktfoto-Rendering
4. Bild wird in S3 gespeichert
5. Figur wird in DB gespeichert
**Output:**
- Fertige Figur mit: Toy-Box-Bild, Name, Rarity, Character-Info, 3 Items
---
### 2. Battle
Zwei Figuren treten gegeneinander an.
**Konzept:**
- User waehlt eigene Figur + Gegner-Figur (aus Community oder eigener Sammlung)
- KI analysiert beide Figuren (Stats, Items, Rarity) und simuliert einen Kampf
- Ergebnis: Kampfbericht + Gewinner + ggf. generiertes Battle-Bild
- Rarity beeinflusst die Kampfstaerke (Legendary > Epic > Rare > Common)
- Items/Artefakte geben Boni
> Details zur Battle-Mechanik (Rundenbasiert? Automatisch? Stats-System?) noch zu definieren.
---
### 3. Merge
Zwei Figuren werden zu einer neuen Figur verschmolzen.
**Konzept:**
- User waehlt zwei eigene Figuren zum Mergen
- KI kombiniert Eigenschaften beider Figuren:
- Visuell: Neues Toy-Box-Bild als Mix beider Figuren
- Character-Info: Merged Lore + kombinierte Items
- Rarity: Chance auf hoeheres Rarity-Level als die Eingangsfiguren
- Ergebnis: Neue, einzigartige Figur
- Originalfiguren bleiben erhalten (oder werden verbraucht? — zu definieren)
> Details zur Merge-Mechanik noch zu definieren.
---
### 4. Figure Detail View
Detailansicht einer einzelnen Figur.
- Grosses Toy-Box-Bild
- Rarity-Badge (farbig)
- Character-Info:
- Description
- Lore / Hintergrundgeschichte
- 3 Items/Artefakte (je mit Name, Description, Lore)
- Like-Button + Like-Count
- Share-Funktion
- Creator-Attribution
- Aktionen: Battle starten, Merge starten
---
### 5. Collection / Shelf
Eigene Figurensammlung.
- Grid-Ansicht aller eigenen Figuren
- Rarity-Badges sichtbar
- Sortierung: Neueste zuerst
- Filter: nach Rarity
- Archivieren (Soft-Delete)
- Schnellzugriff auf Battle / Merge von hier aus
---
### 6. Rarity System
| Rarity | Chance | Farbe | Effekt auf Generierung |
| --------- | ------ | ----- | ----------------------------------------- |
| Common | 60% | Grau | Standard-Detaillevel |
| Rare | 25% | Blau | Mehr Details in Lore + Items |
| Epic | 12% | Lila | Elaborierte Beschreibung, besondere Items |
| Legendary | 3% | Gold | Maximale Detailtiefe, einzigartige Items |
- Beeinflusst KI-Generierung (hoehere Rarity = aufwaendigere Figur)
- Beeinflusst Battle-Staerke
- Beeinflusst Merge-Ergebnis (hoehere Chance auf Upgrade)
- Visuell: Badge-Farben, Card-Border-Effekte, ggf. Glitter/Glow
---
### 7. Settings / Profile
- Dark/Light Mode
- Theme-Auswahl
- Account-Info
- Logout
- Figuren-Statistiken (Anzahl, Raritaeten-Verteilung)
---
## Spaetere Features
| Feature | Beschreibung |
| ------------------ | ----------------------------------------------------------------- |
| **Community Feed** | Oeffentliche Figuren aller User, Likes, Sortierung |
| **Reveal** | Reveal-Mechanik beim Erhalten neuer Figuren (Unboxing-Animation?) |
| **Credits** | Generierung kostet Credits, Free Tier + kaufbare Pakete |
| **Trading** | Figuren zwischen Usern tauschen |
| **Challenges** | Woechentliche Themes ("Erstelle eine Legendary Pirate Figure") |
| **Leaderboards** | Ranglisten: meiste Legendaries, Battle-Wins, etc. |
| **Web App** | SvelteKit Web-Version |
---
## Datenmodell
### Figure
```
id: UUID
name: string
subject: string
imageUrl: string
enhancedPrompt: string (optional)
rarity: 'common' | 'rare' | 'epic' | 'legendary'
characterInfo: {
character: { description, imagePrompt, lore }
items: [{ name, description, imagePrompt, lore }] // 3 Items
styleDescription?: string
}
stats: { // fuer Battle-System
attack: number
defense: number
special: number
}
parentFigures: UUID[] // leer bei generierten, gefuellt bei gemergten
isPublic: boolean
isArchived: boolean
likes: number
userId: string
createdAt: timestamp
updatedAt: timestamp
```
### FigureLike
```
id: UUID
figureId: UUID (FK -> figures)
userId: string
createdAt: timestamp
UNIQUE(figureId, userId)
```
### Battle (spaeter)
```
id: UUID
figure1Id: UUID (FK -> figures)
figure2Id: UUID (FK -> figures)
winnerId: UUID (FK -> figures)
battleLog: JSONB
userId: string
createdAt: timestamp
```
### Merge (spaeter)
```
id: UUID
parent1Id: UUID (FK -> figures)
parent2Id: UUID (FK -> figures)
resultFigureId: UUID (FK -> figures)
userId: string
createdAt: timestamp
```

View file

@ -29,21 +29,21 @@ export default function LoginScreen() {
};
return (
<SafeAreaView className="flex-1 bg-background dark:bg-dark-background">
<SafeAreaView className="flex-1 bg-background">
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
className="flex-1 justify-center px-6"
>
<View className="items-center mb-12">
<Text className="text-4xl font-bold text-primary">Figgos</Text>
<Text className="text-base text-muted mt-2">Collect your fantasy figures</Text>
<Text className="text-base text-muted-foreground mt-2">Collect your action figures</Text>
</View>
<View className="space-y-4">
<TextInput
className="bg-card dark:bg-dark-card border border-border dark:border-dark-border rounded-lg px-4 py-3 text-textColor dark:text-dark-textColor"
className="bg-surface border border-border rounded-lg px-4 py-3 text-foreground"
placeholder="Email"
placeholderTextColor="#B2BEC3"
placeholderTextColor="rgb(99 110 114)"
value={email}
onChangeText={setEmail}
autoCapitalize="none"
@ -51,15 +51,15 @@ export default function LoginScreen() {
/>
<TextInput
className="bg-card dark:bg-dark-card border border-border dark:border-dark-border rounded-lg px-4 py-3 text-textColor dark:text-dark-textColor mt-3"
className="bg-surface border border-border rounded-lg px-4 py-3 text-foreground mt-3"
placeholder="Password"
placeholderTextColor="#B2BEC3"
placeholderTextColor="rgb(99 110 114)"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
{error && <Text className="text-red-500 text-center mt-2">{error}</Text>}
{error && <Text className="text-destructive text-center mt-2">{error}</Text>}
<Pressable
onPress={handleLogin}
@ -68,7 +68,7 @@ export default function LoginScreen() {
`bg-primary rounded-lg py-3 mt-4 ${pressed ? 'opacity-80' : ''} ${loading ? 'opacity-50' : ''}`
}
>
<Text className="text-white text-center font-semibold text-base">
<Text className="text-primary-foreground text-center font-semibold text-base">
{loading ? 'Signing in...' : 'Sign In'}
</Text>
</Pressable>

View file

@ -5,16 +5,16 @@ export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#6C5CE7',
tabBarInactiveTintColor: '#B2BEC3',
tabBarActiveTintColor: 'rgb(108, 92, 231)',
tabBarInactiveTintColor: 'rgb(178, 190, 195)',
tabBarStyle: {
backgroundColor: '#FFFFFF',
borderTopColor: '#DFE6E9',
backgroundColor: 'rgb(255, 255, 255)',
borderTopColor: 'rgb(223, 230, 233)',
},
headerStyle: {
backgroundColor: '#FFFFFF',
backgroundColor: 'rgb(255, 255, 255)',
},
headerTintColor: '#2D3436',
headerTintColor: 'rgb(45, 52, 54)',
}}
>
<Tabs.Screen

View file

@ -3,11 +3,11 @@ import { SafeAreaView } from 'react-native-safe-area-context';
export default function CreateScreen() {
return (
<SafeAreaView className="flex-1 bg-background dark:bg-dark-background" edges={['bottom']}>
<SafeAreaView className="flex-1 bg-background" edges={['bottom']}>
<View className="flex-1 items-center justify-center px-6">
<Text className="text-2xl font-bold text-textColor dark:text-dark-textColor">Create</Text>
<Text className="text-muted mt-2 text-center">
Generate your own AI-powered fantasy figures.
<Text className="text-2xl font-bold text-foreground">Create</Text>
<Text className="text-muted-foreground mt-2 text-center">
Generate your own AI-powered action figures.
</Text>
</View>
</SafeAreaView>

View file

@ -3,12 +3,10 @@ import { SafeAreaView } from 'react-native-safe-area-context';
export default function CommunityScreen() {
return (
<SafeAreaView className="flex-1 bg-background dark:bg-dark-background" edges={['bottom']}>
<SafeAreaView className="flex-1 bg-background" edges={['bottom']}>
<View className="flex-1 items-center justify-center px-6">
<Text className="text-2xl font-bold text-textColor dark:text-dark-textColor">
Community
</Text>
<Text className="text-muted mt-2 text-center">
<Text className="text-2xl font-bold text-foreground">Community</Text>
<Text className="text-muted-foreground mt-2 text-center">
Public figures from the community will appear here.
</Text>
</View>

View file

@ -3,12 +3,10 @@ import { SafeAreaView } from 'react-native-safe-area-context';
export default function ShelfScreen() {
return (
<SafeAreaView className="flex-1 bg-background dark:bg-dark-background" edges={['bottom']}>
<SafeAreaView className="flex-1 bg-background" edges={['bottom']}>
<View className="flex-1 items-center justify-center px-6">
<Text className="text-2xl font-bold text-textColor dark:text-dark-textColor">
My Collection
</Text>
<Text className="text-muted mt-2 text-center">
<Text className="text-2xl font-bold text-foreground">My Collection</Text>
<Text className="text-muted-foreground mt-2 text-center">
Your collected figures will appear here.
</Text>
</View>

View file

@ -5,10 +5,8 @@ export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: 'Not Found' }} />
<View className="flex-1 items-center justify-center bg-background dark:bg-dark-background">
<Text className="text-xl font-bold text-textColor dark:text-dark-textColor">
Page not found
</Text>
<View className="flex-1 items-center justify-center bg-background">
<Text className="text-xl font-bold text-foreground">Page not found</Text>
<Link href="/(tabs)" className="mt-4">
<Text className="text-primary text-base">Go to home</Text>
</Link>

View file

@ -199,10 +199,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#1A1A2E',
backgroundColor: 'rgb(26, 26, 46)',
}}
>
<ActivityIndicator size="large" color="#6C5CE7" />
<ActivityIndicator size="large" color="rgb(108, 92, 231)" />
<Text style={{ marginTop: 16, color: '#fff' }}>Loading...</Text>
</View>
);

View file

@ -1,3 +1,85 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* =============================================================================
Figgos Design System Toy Collectible Theme
Color strategy:
- Primary: Vibrant purple playful, gaming, fun
- Secondary: Electric blue action, energy
- Accent: Teal green success, highlights
- Rarity colors: Common(gray), Rare(blue), Epic(purple), Legendary(gold)
Values are RGB triplets for Tailwind alpha support:
rgb(var(--color) / <alpha-value>)
============================================================================= */
/* Light Mode */
:root {
--background: 248 249 250;
--foreground: 45 52 54;
--surface: 255 255 255;
--surface-elevated: 255 255 255;
--muted: 223 230 233;
--muted-foreground: 99 110 114;
--primary: 108 92 231;
--primary-foreground: 255 255 255;
--secondary: 9 132 227;
--secondary-foreground: 255 255 255;
--accent: 0 184 148;
--accent-foreground: 255 255 255;
--destructive: 231 76 60;
--destructive-foreground: 255 255 255;
--border: 223 230 233;
--input: 223 230 233;
--ring: 108 92 231;
/* Rarity System */
--rarity-common: 178 190 195;
--rarity-rare: 9 132 227;
--rarity-epic: 108 92 231;
--rarity-legendary: 248 214 43;
/* Rarity Text (on rarity background) */
--rarity-common-foreground: 45 52 54;
--rarity-rare-foreground: 255 255 255;
--rarity-epic-foreground: 255 255 255;
--rarity-legendary-foreground: 30 30 30;
}
/* Dark Mode */
.dark {
--background: 26 26 46;
--foreground: 255 255 255;
--surface: 22 33 62;
--surface-elevated: 31 52 96;
--muted: 45 52 54;
--muted-foreground: 178 190 195;
--primary: 162 155 254;
--primary-foreground: 26 26 46;
--secondary: 116 185 255;
--secondary-foreground: 26 26 46;
--accent: 85 239 196;
--accent-foreground: 26 26 46;
--destructive: 255 107 107;
--destructive-foreground: 26 26 46;
--border: 45 52 54;
--input: 45 52 54;
--ring: 162 155 254;
/* Rarity System (brighter in dark mode) */
--rarity-common: 99 110 114;
--rarity-rare: 116 185 255;
--rarity-epic: 162 155 254;
--rarity-legendary: 248 214 43;
--rarity-common-foreground: 255 255 255;
--rarity-rare-foreground: 26 26 46;
--rarity-epic-foreground: 26 26 46;
--rarity-legendary-foreground: 30 30 30;
}

View file

@ -6,24 +6,55 @@ module.exports = {
theme: {
extend: {
colors: {
primary: '#6C5CE7',
secondary: '#A29BFE',
background: '#F8F9FA',
card: '#FFFFFF',
textColor: '#2D3436',
border: '#DFE6E9',
accent: '#00B894',
muted: '#B2BEC3',
// Semantic tokens
background: 'rgb(var(--background) / <alpha-value>)',
foreground: 'rgb(var(--foreground) / <alpha-value>)',
surface: {
DEFAULT: 'rgb(var(--surface) / <alpha-value>)',
elevated: 'rgb(var(--surface-elevated) / <alpha-value>)',
},
muted: {
DEFAULT: 'rgb(var(--muted) / <alpha-value>)',
foreground: 'rgb(var(--muted-foreground) / <alpha-value>)',
},
primary: {
DEFAULT: 'rgb(var(--primary) / <alpha-value>)',
foreground: 'rgb(var(--primary-foreground) / <alpha-value>)',
},
secondary: {
DEFAULT: 'rgb(var(--secondary) / <alpha-value>)',
foreground: 'rgb(var(--secondary-foreground) / <alpha-value>)',
},
accent: {
DEFAULT: 'rgb(var(--accent) / <alpha-value>)',
foreground: 'rgb(var(--accent-foreground) / <alpha-value>)',
},
destructive: {
DEFAULT: 'rgb(var(--destructive) / <alpha-value>)',
foreground: 'rgb(var(--destructive-foreground) / <alpha-value>)',
},
border: 'rgb(var(--border) / <alpha-value>)',
input: 'rgb(var(--input) / <alpha-value>)',
ring: 'rgb(var(--ring) / <alpha-value>)',
dark: {
primary: '#A29BFE',
secondary: '#6C5CE7',
background: '#1A1A2E',
card: '#16213E',
textColor: '#FFFFFF',
border: '#2D3436',
accent: '#55EFC4',
muted: '#636E72',
// Rarity system (game-specific)
rarity: {
common: {
DEFAULT: 'rgb(var(--rarity-common) / <alpha-value>)',
foreground: 'rgb(var(--rarity-common-foreground) / <alpha-value>)',
},
rare: {
DEFAULT: 'rgb(var(--rarity-rare) / <alpha-value>)',
foreground: 'rgb(var(--rarity-rare-foreground) / <alpha-value>)',
},
epic: {
DEFAULT: 'rgb(var(--rarity-epic) / <alpha-value>)',
foreground: 'rgb(var(--rarity-epic-foreground) / <alpha-value>)',
},
legendary: {
DEFAULT: 'rgb(var(--rarity-legendary) / <alpha-value>)',
foreground: 'rgb(var(--rarity-legendary-foreground) / <alpha-value>)',
},
},
},
},