upgrade to tamagui v2 and guest pwa overhaul

This commit is contained in:
Codex Agent
2026-02-02 13:01:20 +01:00
parent 2e78f3ab8d
commit 7c6e14ffe2
168 changed files with 47462 additions and 8914 deletions

View File

@@ -0,0 +1,99 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Camera, UploadCloud, Sparkles, Zap } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function Mockup01CaptureOrbit() {
return (
<MockupFrame
title="Mockup 01 - Capture Orbit"
subtitle="Full-screen capture hub with orbiting actions"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 50% 10%, rgba(248, 113, 113, 0.18), transparent 55%)',
}}
>
<MockupCard padding="$3">
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$3" fontWeight="$7">
Event energy
</Text>
<MockupLabel>87 moments today</MockupLabel>
</YStack>
<XStack alignItems="center" gap="$2">
<Zap size={16} color="#F97316" />
<Text fontSize="$3" fontWeight="$7">
Live
</Text>
</XStack>
</XStack>
</MockupCard>
<YStack alignItems="center" gap="$4">
<YStack
width={260}
height={260}
borderRadius={130}
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
alignItems="center"
justifyContent="center"
gap="$2"
style={{ boxShadow: '0 12px 40px rgba(15, 23, 42, 0.08)' }}
>
<Camera size={32} color="#0F172A" />
<Text fontSize="$5" fontWeight="$8" fontFamily="$display">
Tap to capture
</Text>
<Button size="$4" backgroundColor="$primary" borderRadius="$pill">
Capture
</Button>
</YStack>
<XStack gap="$3">
{[
{ icon: <UploadCloud size={20} color="#0F172A" />, label: 'Upload' },
{ icon: <Sparkles size={20} color="#0F172A" />, label: 'Prompt' },
{ icon: <Camera size={20} color="#0F172A" />, label: 'Burst' },
].map((item) => (
<YStack
key={item.label}
width={72}
height={72}
borderRadius={36}
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
alignItems="center"
justifyContent="center"
gap="$1"
>
{item.icon}
<Text fontSize="$2" fontWeight="$6">
{item.label}
</Text>
</YStack>
))}
</XStack>
</YStack>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$3" fontWeight="$7">
Upload queue
</Text>
<MockupLabel>3 queued, 1 sending</MockupLabel>
</YStack>
<Button size="$3" backgroundColor="$surface" borderRadius="$pill" borderWidth={1} borderColor="$borderColor">
View
</Button>
</XStack>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,76 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Filter, Image as ImageIcon } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupChip, MockupLabel, MockupTile } from './MockupPrimitives';
export default function Mockup02GalleryMosaic() {
return (
<MockupFrame
title="Mockup 02 - Gallery Mosaic"
subtitle="Image-first grid with fast filters"
backgroundStyle={{
backgroundImage: 'linear-gradient(160deg, rgba(248, 250, 252, 0.9), rgba(255, 245, 240, 0.9))',
}}
>
<MockupCard padding="$3">
<XStack alignItems="center" justifyContent="space-between">
<XStack alignItems="center" gap="$2">
<ImageIcon size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$8">
Gallery
</Text>
</XStack>
<Filter size={18} color="#0F172A" />
</XStack>
<XStack gap="$2" flexWrap="wrap">
{['All', 'Friends', 'Dancefloor', 'Candid'].map((chip) => (
<MockupChip key={chip}>
<Text fontSize="$2" fontWeight="$6">
{chip}
</Text>
</MockupChip>
))}
</XStack>
</MockupCard>
<XStack gap="$3">
<YStack flex={1} gap="$3">
<MockupTile height={160}>
<MockupLabel>Hero moment</MockupLabel>
</MockupTile>
<XStack gap="$3">
<MockupTile flex={1} height={90} />
<MockupTile flex={1} height={90} />
</XStack>
<MockupTile height={120} />
</YStack>
<YStack flex={1} gap="$3">
<XStack gap="$3">
<MockupTile flex={1} height={110} />
<MockupTile flex={1} height={110} />
</XStack>
<MockupTile height={180}>
<MockupLabel>Live spotlight</MockupLabel>
</MockupTile>
<MockupTile height={90} />
</YStack>
</XStack>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$3" fontWeight="$7">
Newest uploads
</Text>
<MockupLabel>Refreshes every 10s</MockupLabel>
</YStack>
<Text fontSize="$2" fontWeight="$7">
22 new
</Text>
</XStack>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,74 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Sparkles, Trophy, Play } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function Mockup03PromptQuest() {
return (
<MockupFrame
title="Mockup 03 - Prompt Quest"
subtitle="Prompt hero with progress and task ladder"
>
<MockupCard>
<XStack alignItems="center" gap="$2">
<Sparkles size={18} color="#F43F5E" />
<Text fontSize="$3" fontWeight="$7">
Prompt of the hour
</Text>
</XStack>
<Text fontSize="$7" fontFamily="$display" fontWeight="$8">
Capture the boldest dance move
</Text>
<MockupLabel>Earn 120 points for completing this prompt.</MockupLabel>
<YStack gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$2" fontWeight="$7">
Quest progress
</Text>
<Text fontSize="$2" fontWeight="$7">
65%
</Text>
</XStack>
<YStack backgroundColor="$muted" borderRadius="$pill" height={10} overflow="hidden">
<YStack backgroundColor="$primary" width="65%" height={10} />
</YStack>
</YStack>
<Button size="$4" backgroundColor="$primary" borderRadius="$pill">
Start capture
</Button>
</MockupCard>
<YStack gap="$3">
{[1, 2, 3].map((item) => (
<MockupCard key={item} padding="$3">
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$4" fontWeight="$7">
Task {item}
</Text>
<MockupLabel>Quick challenge to spark the moment.</MockupLabel>
</YStack>
<XStack alignItems="center" gap="$2">
<Trophy size={16} color="#F59E0B" />
<Text fontSize="$2" fontWeight="$7">
+50
</Text>
</XStack>
</XStack>
<Button size="$3" backgroundColor="$surface" borderRadius="$pill" borderWidth={1} borderColor="$borderColor">
<XStack alignItems="center" gap="$2">
<Play size={14} color="#0F172A" />
<Text fontSize="$2" fontWeight="$7">
Play task
</Text>
</XStack>
</Button>
</MockupCard>
))}
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,70 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Clock, Image as ImageIcon } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
const timeline = [
{ time: '18:40', title: 'Sparkler exit', caption: '12 new uploads' },
{ time: '18:22', title: 'First dance', caption: '7 moments added' },
{ time: '18:10', title: 'Toast round', caption: '5 guest favorites' },
{ time: '17:55', title: 'Arrival glow', caption: '4 candid shots' },
];
export default function Mockup04TimelineStream() {
return (
<MockupFrame
title="Mockup 04 - Timeline Stream"
subtitle="Chronological feed with time markers"
backgroundStyle={{
backgroundImage: 'linear-gradient(180deg, rgba(254, 252, 232, 0.9), rgba(255, 255, 255, 0.95))',
}}
>
<MockupCard padding="$3">
<XStack alignItems="center" gap="$2">
<Clock size={16} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
Live timeline
</Text>
</XStack>
<MockupLabel>Moments grouped by when they happened.</MockupLabel>
</MockupCard>
<YStack gap="$3">
{timeline.map((item, index) => (
<XStack key={item.time} gap="$3" alignItems="flex-start">
<YStack alignItems="center" gap="$2" width={60}>
<Text fontSize="$2" fontWeight="$7">
{item.time}
</Text>
<YStack width={2} flex={1} backgroundColor="$borderColor" opacity={index === timeline.length - 1 ? 0 : 1} />
</YStack>
<MockupCard flex={1} padding="$3">
<XStack alignItems="center" gap="$2">
<ImageIcon size={16} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
{item.title}
</Text>
</XStack>
<MockupLabel>{item.caption}</MockupLabel>
<XStack gap="$2">
{[1, 2, 3].map((tile) => (
<YStack
key={tile}
flex={1}
height={56}
borderRadius="$tile"
backgroundColor="$muted"
borderWidth={1}
borderColor="$borderColor"
/>
))}
</XStack>
</MockupCard>
</XStack>
))}
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,81 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Compass, Image as ImageIcon, Sparkles, UploadCloud } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function Mockup05CompassHub() {
return (
<MockupFrame
title="Mockup 05 - Compass Hub"
subtitle="Quadrant navigation around a central action"
>
<YStack position="relative" height={360} gap="$3">
<XStack gap="$3">
<MockupCard flex={1} height={150} justifyContent="center" alignItems="center">
<ImageIcon size={20} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Gallery
</Text>
<MockupLabel>Browse moments</MockupLabel>
</MockupCard>
<MockupCard flex={1} height={150} justifyContent="center" alignItems="center">
<UploadCloud size={20} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Upload
</Text>
<MockupLabel>Quick add</MockupLabel>
</MockupCard>
</XStack>
<XStack gap="$3">
<MockupCard flex={1} height={150} justifyContent="center" alignItems="center">
<Sparkles size={20} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Tasks
</Text>
<MockupLabel>Earn points</MockupLabel>
</MockupCard>
<MockupCard flex={1} height={150} justifyContent="center" alignItems="center">
<Compass size={20} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Live show
</Text>
<MockupLabel>See highlights</MockupLabel>
</MockupCard>
</XStack>
<YStack
position="absolute"
top="50%"
left="50%"
width={110}
height={110}
borderRadius={55}
backgroundColor="$primary"
alignItems="center"
justifyContent="center"
style={{ transform: 'translate(-50%, -50%)' }}
>
<Text fontSize="$4" fontWeight="$8" color="white">
Capture
</Text>
</YStack>
</YStack>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$3" fontWeight="$7">
Event compass
</Text>
<MockupLabel>Tap any quadrant to jump.</MockupLabel>
</YStack>
<Text fontSize="$2" fontWeight="$7">
4 zones
</Text>
</XStack>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,66 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Camera, Grid2x2, Zap, UploadCloud } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel, MockupTile } from './MockupPrimitives';
export default function Mockup06SplitCapture() {
return (
<MockupFrame
title="Mockup 06 - Split Capture"
subtitle="Camera preview plus quick tools and queue"
backgroundStyle={{
backgroundImage: 'linear-gradient(180deg, rgba(240, 249, 255, 0.95), rgba(255, 255, 255, 0.95))',
}}
>
<YStack gap="$3">
<YStack
height={260}
borderRadius="$card"
backgroundColor="$muted"
borderWidth={1}
borderColor="$borderColor"
alignItems="center"
justifyContent="center"
gap="$2"
>
<Camera size={32} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
Live preview
</Text>
<MockupLabel>Tap anywhere to focus.</MockupLabel>
</YStack>
<XStack gap="$3">
{[
{ icon: <Grid2x2 size={18} color="#0F172A" />, label: 'Grid' },
{ icon: <Zap size={18} color="#F97316" />, label: 'Flash' },
{ icon: <UploadCloud size={18} color="#0F172A" />, label: 'Upload' },
].map((tool) => (
<MockupCard key={tool.label} flex={1} padding="$3" alignItems="center">
{tool.icon}
<Text fontSize="$3" fontWeight="$7">
{tool.label}
</Text>
</MockupCard>
))}
</XStack>
</YStack>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$4" fontWeight="$7">
Upload queue
</Text>
<MockupLabel>2 waiting</MockupLabel>
</XStack>
<XStack gap="$2">
{[1, 2, 3].map((tile) => (
<MockupTile key={tile} flex={1} height={72} />
))}
</XStack>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,94 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Heart, X, Send } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function Mockup07SwipeDeck() {
return (
<MockupFrame
title="Mockup 07 - Swipe Deck"
subtitle="Stacked cards for rapid review"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 80% 0%, rgba(190, 24, 93, 0.12), transparent 50%)',
}}
>
<MockupCard padding="$3">
<Text fontSize="$4" fontWeight="$7">
Review queue
</Text>
<MockupLabel>Swipe to approve or skip highlights.</MockupLabel>
</MockupCard>
<YStack height={360} position="relative">
<MockupCard
height={320}
position="absolute"
left={0}
right={0}
style={{ transform: 'translateY(24px) rotate(-2deg)' }}
>
<Text fontSize="$4" fontWeight="$7">
Guest capture 3
</Text>
</MockupCard>
<MockupCard
height={330}
position="absolute"
left={0}
right={0}
style={{ transform: 'translateY(12px) rotate(1deg)' }}
>
<Text fontSize="$4" fontWeight="$7">
Guest capture 2
</Text>
</MockupCard>
<MockupCard height={340} position="absolute" left={0} right={0}>
<Text fontSize="$6" fontFamily="$display" fontWeight="$8">
Guest capture 1
</Text>
<MockupLabel>Tap for full view</MockupLabel>
<YStack flex={1} borderRadius="$card" backgroundColor="$muted" />
</MockupCard>
</YStack>
<XStack gap="$3" justifyContent="center">
<YStack
width={64}
height={64}
borderRadius={32}
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
alignItems="center"
justifyContent="center"
>
<X size={20} color="#EF4444" />
</YStack>
<YStack
width={72}
height={72}
borderRadius={36}
backgroundColor="$primary"
alignItems="center"
justifyContent="center"
>
<Heart size={24} color="white" />
</YStack>
<YStack
width={64}
height={64}
borderRadius={32}
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
alignItems="center"
justifyContent="center"
>
<Send size={20} color="#0F172A" />
</YStack>
</XStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,58 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Sun, CloudSun, MoonStar } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel, MockupTile } from './MockupPrimitives';
const sections = [
{
key: 'morning',
title: 'Morning glow',
icon: <Sun size={18} color="#F97316" />,
},
{
key: 'afternoon',
title: 'Afternoon energy',
icon: <CloudSun size={18} color="#F59E0B" />,
},
{
key: 'night',
title: 'Night sparkle',
icon: <MoonStar size={18} color="#6366F1" />,
},
];
export default function Mockup08Daybook() {
return (
<MockupFrame
title="Mockup 08 - Daybook"
subtitle="Morning/afternoon/night memory sections"
>
<MockupCard>
<Text fontSize="$5" fontFamily="$display" fontWeight="$8">
Today in moments
</Text>
<MockupLabel>Group memories by vibe, not just time.</MockupLabel>
</MockupCard>
<YStack gap="$4">
{sections.map((section) => (
<YStack key={section.key} gap="$2">
<XStack alignItems="center" gap="$2">
{section.icon}
<Text fontSize="$4" fontWeight="$7">
{section.title}
</Text>
</XStack>
<XStack gap="$2">
{[1, 2, 3].map((tile) => (
<MockupTile key={tile} flex={1} height={110} />
))}
</XStack>
</YStack>
))}
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { CheckCircle2, Circle, ListChecks } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel, MockupTile } from './MockupPrimitives';
const tasks = [
{ id: 1, label: 'Capture the first toast', done: true },
{ id: 2, label: 'Find the loudest laugh', done: false },
{ id: 3, label: 'Snap a detail shot', done: false },
{ id: 4, label: 'Find the dance duo', done: true },
];
export default function Mockup09ChecklistFlow() {
return (
<MockupFrame
title="Mockup 09 - Checklist Flow"
subtitle="Tasks checklist paired with gallery progress"
backgroundStyle={{
backgroundImage: 'linear-gradient(180deg, rgba(244, 244, 255, 0.8), rgba(255, 255, 255, 0.9))',
}}
>
<MockupCard>
<XStack alignItems="center" gap="$2">
<ListChecks size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
Todays checklist
</Text>
</XStack>
<MockupLabel>Complete tasks to unlock more prompts.</MockupLabel>
</MockupCard>
<MockupCard>
<YStack gap="$2">
{tasks.map((task) => (
<XStack key={task.id} alignItems="center" gap="$2">
{task.done ? (
<CheckCircle2 size={18} color="#22C55E" />
) : (
<Circle size={18} color="#94A3B8" />
)}
<Text fontSize="$3" fontWeight="$6">
{task.label}
</Text>
</XStack>
))}
</YStack>
</MockupCard>
<MockupCard>
<Text fontSize="$3" fontWeight="$7">
Progress gallery
</Text>
<XStack gap="$2">
{[1, 2, 3, 4].map((tile) => (
<MockupTile key={tile} flex={1} height={70} />
))}
</XStack>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,62 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Play, Star, Share2 } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel, MockupTile } from './MockupPrimitives';
export default function Mockup10SpotlightReel() {
return (
<MockupFrame
title="Mockup 10 - Spotlight Reel"
subtitle="Live highlight reel with action strip"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 20% 0%, rgba(251, 191, 36, 0.2), transparent 55%)',
}}
>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$4" fontWeight="$7">
Spotlight reel
</Text>
<MockupLabel>Live highlights curated by guests.</MockupLabel>
</YStack>
<Play size={18} color="#0F172A" />
</XStack>
</MockupCard>
<XStack gap="$2">
{[1, 2, 3, 4, 5].map((tile) => (
<MockupTile key={tile} flex={1} height={70} />
))}
</XStack>
<MockupCard>
<Text fontSize="$6" fontFamily="$display" fontWeight="$8">
Now showing
</Text>
<YStack height={200} borderRadius="$card" backgroundColor="$muted" />
<XStack alignItems="center" justifyContent="space-between">
<XStack alignItems="center" gap="$2">
<Star size={16} color="#F59E0B" />
<Text fontSize="$3" fontWeight="$7">
4.8 rating
</Text>
</XStack>
<Share2 size={16} color="#0F172A" />
</XStack>
</MockupCard>
<XStack gap="$2">
{['Like', 'Save', 'Share'].map((action) => (
<MockupCard key={action} flex={1} padding="$3" alignItems="center">
<Text fontSize="$3" fontWeight="$7">
{action}
</Text>
</MockupCard>
))}
</XStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { XStack, YStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { ArrowLeft } from 'lucide-react';
type MockupFrameProps = {
title: string;
subtitle?: string;
hideHeader?: boolean;
backgroundStyle?: React.CSSProperties;
children: React.ReactNode;
};
export default function MockupFrame({
title,
subtitle,
hideHeader,
backgroundStyle,
children,
}: MockupFrameProps) {
const navigate = useNavigate();
return (
<YStack
minHeight="100vh"
backgroundColor="$background"
padding="$4"
gap="$4"
style={backgroundStyle}
>
{!hideHeader && (
<XStack alignItems="center" justifyContent="space-between" gap="$3">
<Button
size="$3"
backgroundColor="$surface"
borderRadius="$pill"
borderWidth={1}
borderColor="$borderColor"
onPress={() => navigate('/mockups')}
>
<XStack alignItems="center" gap="$2">
<ArrowLeft size={16} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Back
</Text>
</XStack>
</Button>
<YStack alignItems="flex-end" gap="$1">
<Text fontSize="$5" fontWeight="$8" fontFamily="$display">
{title}
</Text>
{subtitle ? (
<Text fontSize="$2" color="$color" opacity={0.6}>
{subtitle}
</Text>
) : null}
</YStack>
</XStack>
)}
{children}
</YStack>
);
}

View File

@@ -0,0 +1,70 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Camera, Zap, Users } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function MockupHome01PulseHero() {
return (
<MockupFrame
title="Home 01 - Pulse Hero"
subtitle="Live stats + big capture call-to-action"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 20% 0%, rgba(59, 130, 246, 0.16), transparent 50%)',
}}
>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$3" fontWeight="$7">
Sofia + Luca
</Text>
<MockupLabel>Reception in progress</MockupLabel>
</YStack>
<XStack alignItems="center" gap="$2">
<Zap size={16} color="#F97316" />
<Text fontSize="$3" fontWeight="$7">
Live
</Text>
</XStack>
</XStack>
</MockupCard>
<YStack
padding="$5"
borderRadius="$card"
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
alignItems="center"
gap="$3"
>
<Camera size={28} color="#0F172A" />
<Text fontSize="$7" fontFamily="$display" fontWeight="$8">
Capture the next moment
</Text>
<MockupLabel>Share it instantly with the room.</MockupLabel>
<Button size="$4" backgroundColor="$primary" borderRadius="$pill">
Start capture
</Button>
</YStack>
<XStack gap="$3">
<MockupCard flex={1} alignItems="center">
<Text fontSize="$6" fontWeight="$8">
128
</Text>
<MockupLabel>Photos today</MockupLabel>
</MockupCard>
<MockupCard flex={1} alignItems="center">
<Users size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
54 guests
</Text>
</MockupCard>
</XStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,75 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Camera, Sparkles, Image as ImageIcon, Users } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
const rings = [
{ label: 'Capture', icon: <Camera size={18} color="#0F172A" /> },
{ label: 'Prompts', icon: <Sparkles size={18} color="#0F172A" /> },
{ label: 'Gallery', icon: <ImageIcon size={18} color="#0F172A" /> },
{ label: 'Guests', icon: <Users size={18} color="#0F172A" /> },
];
export default function MockupHome02StoryRings() {
return (
<MockupFrame
title="Home 02 - Story Rings"
subtitle="Circular quick actions with story chips"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 80% 0%, rgba(244, 63, 94, 0.14), transparent 55%)',
}}
>
<MockupCard>
<Text fontSize="$5" fontFamily="$display" fontWeight="$8">
Morning glow
</Text>
<MockupLabel>Tap a ring to jump in.</MockupLabel>
</MockupCard>
<XStack justifyContent="space-between">
{rings.map((ring) => (
<YStack key={ring.label} alignItems="center" gap="$2">
<YStack
width={72}
height={72}
borderRadius={36}
backgroundColor="$surface"
borderWidth={2}
borderColor="$primary"
alignItems="center"
justifyContent="center"
>
{ring.icon}
</YStack>
<Text fontSize="$2" fontWeight="$6">
{ring.label}
</Text>
</YStack>
))}
</XStack>
<YStack gap="$3">
<MockupCard>
<Text fontSize="$4" fontWeight="$7">
Story highlights
</Text>
<XStack gap="$2">
{[1, 2, 3].map((chip) => (
<YStack
key={chip}
flex={1}
height={90}
borderRadius="$tile"
backgroundColor="$muted"
borderWidth={1}
borderColor="$borderColor"
/>
))}
</XStack>
</MockupCard>
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,52 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Play, Share2, Cast } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel, MockupTile } from './MockupPrimitives';
export default function MockupHome03LiveStream() {
return (
<MockupFrame
title="Home 03 - Live Stream"
subtitle="Highlight reel + action strip"
backgroundStyle={{
backgroundImage: 'linear-gradient(180deg, rgba(254, 242, 242, 0.9), rgba(255, 255, 255, 0.95))',
}}
>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<XStack alignItems="center" gap="$2">
<Cast size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
Live highlight stream
</Text>
</XStack>
<Text fontSize="$2" fontWeight="$7">
24 now
</Text>
</XStack>
<MockupLabel>Tap to join the live wall.</MockupLabel>
</MockupCard>
<MockupCard>
<YStack height={210} borderRadius="$card" backgroundColor="$muted" />
<XStack alignItems="center" justifyContent="space-between">
<XStack alignItems="center" gap="$2">
<Play size={16} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Now playing
</Text>
</XStack>
<Share2 size={16} color="#0F172A" />
</XStack>
</MockupCard>
<XStack gap="$2">
{[1, 2, 3, 4].map((tile) => (
<MockupTile key={tile} flex={1} height={70} />
))}
</XStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Sparkles, Trophy } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function MockupHome04TaskSprint() {
return (
<MockupFrame
title="Home 04 - Task Sprint"
subtitle="Prompt ladder + progress meter"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 20% 0%, rgba(167, 139, 250, 0.2), transparent 55%)',
}}
>
<MockupCard>
<XStack alignItems="center" gap="$2">
<Sparkles size={18} color="#8B5CF6" />
<Text fontSize="$4" fontWeight="$7">
Sprint of the hour
</Text>
</XStack>
<Text fontSize="$6" fontFamily="$display" fontWeight="$8">
Capture three smiles in 5 minutes
</Text>
<MockupLabel>Earn double points for finishing early.</MockupLabel>
<YStack gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$2" fontWeight="$7">
Progress
</Text>
<Text fontSize="$2" fontWeight="$7">
2/3
</Text>
</XStack>
<YStack backgroundColor="$muted" borderRadius="$pill" height={10} overflow="hidden">
<YStack backgroundColor="$primary" width="66%" height={10} />
</YStack>
</YStack>
<Button size="$4" backgroundColor="$primary" borderRadius="$pill">
Continue sprint
</Button>
</MockupCard>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$3" fontWeight="$7">
Rewards
</Text>
<XStack alignItems="center" gap="$2">
<Trophy size={16} color="#F59E0B" />
<Text fontSize="$2" fontWeight="$7">
+180
</Text>
</XStack>
</XStack>
<MockupLabel>Unlock more prompts after this sprint.</MockupLabel>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,46 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Image as ImageIcon, Filter } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel, MockupTile } from './MockupPrimitives';
export default function MockupHome05GalleryFirst() {
return (
<MockupFrame
title="Home 05 - Gallery First"
subtitle="Grid preview + quick filters"
>
<MockupCard padding="$3">
<XStack alignItems="center" justifyContent="space-between">
<XStack alignItems="center" gap="$2">
<ImageIcon size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
Moments
</Text>
</XStack>
<Filter size={16} color="#0F172A" />
</XStack>
<MockupLabel>Start browsing before you upload.</MockupLabel>
</MockupCard>
<XStack gap="$2">
{[1, 2, 3].map((tile) => (
<MockupTile key={tile} flex={1} height={100} />
))}
</XStack>
<XStack gap="$2">
{[4, 5, 6].map((tile) => (
<MockupTile key={tile} flex={1} height={120} />
))}
</XStack>
<MockupCard>
<Text fontSize="$3" fontWeight="$7">
Latest uploads
</Text>
<MockupLabel>Updated every 15 seconds.</MockupLabel>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,56 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Camera, CheckCircle2 } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function MockupHome06CalmFocus() {
return (
<MockupFrame
title="Home 06 - Calm Focus"
subtitle="Minimal home with one primary action"
backgroundStyle={{
backgroundImage: 'linear-gradient(180deg, rgba(236, 254, 255, 0.9), rgba(255, 255, 255, 0.95))',
}}
>
<YStack
padding="$5"
borderRadius="$card"
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
gap="$3"
>
<Text fontSize="$6" fontFamily="$display" fontWeight="$8">
Capture something quiet
</Text>
<MockupLabel>One great photo beats five ok ones.</MockupLabel>
<Button size="$4" backgroundColor="$primary" borderRadius="$pill">
<XStack alignItems="center" gap="$2">
<Camera size={18} color="white" />
<Text fontSize="$3" color="white">
Open camera
</Text>
</XStack>
</Button>
</YStack>
<MockupCard>
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$3" fontWeight="$7">
Uploads today
</Text>
<XStack alignItems="center" gap="$2">
<CheckCircle2 size={16} color="#22C55E" />
<Text fontSize="$2" fontWeight="$7">
12 approved
</Text>
</XStack>
</XStack>
<MockupLabel>Everything is synced and safe.</MockupLabel>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Camera, ArrowUpRight } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function MockupHome07MomentStack() {
return (
<MockupFrame
title="Home 07 - Moment Stack"
subtitle="Stacked cards for rapid capture"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 70% 0%, rgba(14, 165, 233, 0.15), transparent 50%)',
}}
>
<MockupCard>
<Text fontSize="$5" fontFamily="$display" fontWeight="$8">
Moment stack
</Text>
<MockupLabel>Start at the top and keep capturing.</MockupLabel>
</MockupCard>
<YStack height={320} position="relative">
<MockupCard
height={220}
position="absolute"
left={0}
right={0}
style={{ transform: 'translateY(28px) rotate(-2deg)' }}
>
<Text fontSize="$4" fontWeight="$7">
Prompt: hands in the air
</Text>
</MockupCard>
<MockupCard
height={230}
position="absolute"
left={0}
right={0}
style={{ transform: 'translateY(14px) rotate(1deg)' }}
>
<Text fontSize="$4" fontWeight="$7">
Prompt: candid laugh
</Text>
</MockupCard>
<MockupCard height={240} position="absolute" left={0} right={0}>
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$5" fontWeight="$8">
Prompt: dance floor
</Text>
<ArrowUpRight size={18} color="#0F172A" />
</XStack>
<MockupLabel>Tap to start this capture.</MockupLabel>
<XStack alignItems="center" gap="$2">
<Camera size={18} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Open camera
</Text>
</XStack>
</MockupCard>
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,54 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Clock, Play } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function MockupHome08CountdownStage() {
return (
<MockupFrame
title="Home 08 - Countdown Stage"
subtitle="Event timing + live show entry"
backgroundStyle={{
backgroundImage: 'linear-gradient(180deg, rgba(254, 249, 195, 0.9), rgba(255, 255, 255, 0.95))',
}}
>
<MockupCard>
<XStack alignItems="center" gap="$2">
<Clock size={16} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Next highlight in
</Text>
</XStack>
<Text fontSize="$7" fontFamily="$display" fontWeight="$8">
04:32
</Text>
<MockupLabel>Stay ready for the next big moment.</MockupLabel>
</MockupCard>
<YStack
padding="$4"
borderRadius="$card"
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
gap="$3"
>
<Text fontSize="$4" fontWeight="$7">
Live show stage
</Text>
<MockupLabel>Jump into the projected highlight wall.</MockupLabel>
<Button size="$4" backgroundColor="$primary" borderRadius="$pill">
<XStack alignItems="center" gap="$2">
<Play size={16} color="white" />
<Text fontSize="$3" color="white">
Open live show
</Text>
</XStack>
</Button>
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Share2, QrCode, Link } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
export default function MockupHome09ShareHub() {
return (
<MockupFrame
title="Home 09 - Share Hub"
subtitle="Invite + QR + guest sharing tools"
backgroundStyle={{
backgroundImage: 'radial-gradient(circle at 25% 0%, rgba(14, 165, 233, 0.18), transparent 50%)',
}}
>
<MockupCard>
<XStack alignItems="center" gap="$2">
<Share2 size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
Invite guests
</Text>
</XStack>
<MockupLabel>Share the link or show the QR code.</MockupLabel>
</MockupCard>
<XStack gap="$3">
<MockupCard flex={1} alignItems="center" gap="$2">
<QrCode size={24} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Show QR
</Text>
<MockupLabel>Scan to join</MockupLabel>
</MockupCard>
<MockupCard flex={1} alignItems="center" gap="$2">
<Link size={24} color="#0F172A" />
<Text fontSize="$3" fontWeight="$7">
Copy link
</Text>
<MockupLabel>Send in chat</MockupLabel>
</MockupCard>
</XStack>
<YStack
padding="$4"
borderRadius="$card"
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
gap="$3"
>
<Text fontSize="$4" fontWeight="$7">
Welcome pack
</Text>
<MockupLabel>Tips for guests and upload etiquette.</MockupLabel>
<Button size="$3" backgroundColor="$primary" borderRadius="$pill" alignSelf="flex-start">
Open guide
</Button>
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,56 @@
import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Palette, Sparkles } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel, MockupTile } from './MockupPrimitives';
const palettes = ['#FDE68A', '#F9A8D4', '#A5B4FC', '#6EE7B7', '#FCA5A5'];
export default function MockupHome10Moodboard() {
return (
<MockupFrame
title="Home 10 - Moodboard"
subtitle="Palette + prompts to set the vibe"
backgroundStyle={{
backgroundImage: 'linear-gradient(180deg, rgba(255, 247, 237, 0.95), rgba(255, 255, 255, 0.95))',
}}
>
<MockupCard>
<XStack alignItems="center" gap="$2">
<Palette size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$7">
Moodboard
</Text>
</XStack>
<MockupLabel>Pick a vibe for the next wave of shots.</MockupLabel>
</MockupCard>
<XStack gap="$2">
{palettes.map((color) => (
<YStack
key={color}
flex={1}
height={54}
borderRadius="$pill"
style={{ backgroundColor: color }}
/>
))}
</XStack>
<MockupCard>
<XStack alignItems="center" gap="$2">
<Sparkles size={16} color="#F43F5E" />
<Text fontSize="$3" fontWeight="$7">
Prompt ideas
</Text>
</XStack>
<XStack gap="$2">
{[1, 2, 3].map((tile) => (
<MockupTile key={tile} flex={1} height={80} />
))}
</XStack>
</MockupCard>
</MockupFrame>
);
}

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { XStack, YStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
type StackProps = React.ComponentProps<typeof YStack>;
type RowProps = React.ComponentProps<typeof XStack>;
export function MockupCard({ children, ...props }: StackProps) {
return (
<YStack
padding="$4"
borderRadius="$card"
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
gap="$3"
{...props}
>
{children}
</YStack>
);
}
export function MockupTile({ children, ...props }: StackProps) {
return (
<YStack
padding="$3"
borderRadius="$tile"
backgroundColor="$muted"
borderWidth={1}
borderColor="$borderColor"
gap="$2"
{...props}
>
{children}
</YStack>
);
}
export function MockupChip({ children, ...props }: RowProps) {
return (
<XStack
alignItems="center"
gap="$2"
paddingVertical="$2"
paddingHorizontal="$3"
borderRadius="$pill"
backgroundColor="$surface"
borderWidth={1}
borderColor="$borderColor"
{...props}
>
{children}
</XStack>
);
}
export function MockupLabel({ children }: { children: React.ReactNode }) {
return (
<Text fontSize="$2" color="$color" opacity={0.6}>
{children}
</Text>
);
}

View File

@@ -0,0 +1,68 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { ArrowRight, Home } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
const mockups = [
{ id: '1', title: 'Pulse Hero', description: 'Live stats + big capture call-to-action.' },
{ id: '2', title: 'Story Rings', description: 'Circular quick actions with story chips.' },
{ id: '3', title: 'Live Stream', description: 'Highlight reel + action strip.' },
{ id: '4', title: 'Task Sprint', description: 'Prompt ladder + progress meter.' },
{ id: '5', title: 'Gallery First', description: 'Grid preview + quick filters.' },
{ id: '6', title: 'Calm Focus', description: 'Minimal home with one primary action.' },
{ id: '7', title: 'Moment Stack', description: 'Stacked cards for rapid capture.' },
{ id: '8', title: 'Countdown Stage', description: 'Event timing + live show entry.' },
{ id: '9', title: 'Share Hub', description: 'Invite + QR + guest sharing tools.' },
{ id: '10', title: 'Moodboard', description: 'Palette + prompts to set the vibe.' },
];
export default function MockupsHomeIndexScreen() {
const navigate = useNavigate();
return (
<MockupFrame
title="Home concepts"
subtitle="10 start screen ideas for the new guest PWA"
>
<MockupCard padding="$3">
<XStack alignItems="center" gap="$2">
<Home size={18} color="#0F172A" />
<Text fontSize="$4" fontWeight="$8">
Start screen concepts
</Text>
</XStack>
<MockupLabel>Pick one as the north star for v2.</MockupLabel>
</MockupCard>
<YStack gap="$3">
{mockups.map((mockup) => (
<MockupCard key={mockup.id} gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$5" fontWeight="$8" fontFamily="$display">
{mockup.title}
</Text>
<Button
size="$3"
backgroundColor="$primary"
borderRadius="$pill"
onPress={() => navigate(`/mockups/home/${mockup.id}`)}
>
<XStack alignItems="center" gap="$2">
<Text fontSize="$3" color="white">
View
</Text>
<ArrowRight size={16} color="white" />
</XStack>
</Button>
</XStack>
<MockupLabel>{mockup.description}</MockupLabel>
</MockupCard>
))}
</YStack>
</MockupFrame>
);
}

View File

@@ -0,0 +1,82 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { ArrowRight } from 'lucide-react';
import MockupFrame from './MockupFrame';
import { MockupCard, MockupLabel } from './MockupPrimitives';
const mockups = [
{ id: '1', title: 'Capture Orbit', description: 'Full-screen capture hub with orbiting actions.' },
{ id: '2', title: 'Gallery Mosaic', description: 'Image-first grid with filters and fast scan.' },
{ id: '3', title: 'Prompt Quest', description: 'Prompt hero with progress and task ladder.' },
{ id: '4', title: 'Timeline Stream', description: 'Chronological feed with time markers.' },
{ id: '5', title: 'Compass Hub', description: 'Quadrant navigation around a central action.' },
{ id: '6', title: 'Split Capture', description: 'Camera preview plus quick tools and queue.' },
{ id: '7', title: 'Swipe Deck', description: 'Stacked cards for rapid review.' },
{ id: '8', title: 'Daybook', description: 'Morning/afternoon/night memory sections.' },
{ id: '9', title: 'Checklist Flow', description: 'Tasks checklist paired with gallery progress.' },
{ id: '10', title: 'Spotlight Reel', description: 'Live highlight reel with action strip.' },
];
export default function MockupsIndexScreen() {
const navigate = useNavigate();
return (
<MockupFrame
title="Guest PWA mockups"
subtitle="10 layout concepts for the new event experience"
>
<MockupCard gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<YStack gap="$1">
<Text fontSize="$5" fontWeight="$8" fontFamily="$display">
Home concepts
</Text>
<MockupLabel>Explore 10 start screen ideas.</MockupLabel>
</YStack>
<Button
size="$3"
backgroundColor="$primary"
borderRadius="$pill"
onPress={() => navigate('/mockups/home')}
>
<XStack alignItems="center" gap="$2">
<Text fontSize="$3" color="white">
Open
</Text>
<ArrowRight size={16} color="white" />
</XStack>
</Button>
</XStack>
</MockupCard>
<YStack gap="$3">
{mockups.map((mockup) => (
<MockupCard key={mockup.id} gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$5" fontWeight="$8" fontFamily="$display">
{mockup.title}
</Text>
<Button
size="$3"
backgroundColor="$primary"
borderRadius="$pill"
onPress={() => navigate(`/mockups/${mockup.id}`)}
>
<XStack alignItems="center" gap="$2">
<Text fontSize="$3" color="white">
View
</Text>
<ArrowRight size={16} color="white" />
</XStack>
</Button>
</XStack>
<MockupLabel>{mockup.description}</MockupLabel>
</MockupCard>
))}
</YStack>
</MockupFrame>
);
}