Di Gusto Components

Developer reference for all React Native components in the Di Gusto platform. Built with Expo, React Native Paper, and IBM Plex Sans. This library powers the immersive Italian storytelling experience.

28Components
6Categories
RNPlatform
MD3Design

Core Components

Avatar components/Avatar.tsx

Circular initial-based avatar with optional gradient background and auteur level badge. Displays 1–2 characters in a colored circle. The optional level prop (1–3) renders a small colored badge at the bottom-right: gray (Audience), amber (Director), orange (Auteur).

Props
PropTypeDefaultDescription
labelrequiredstring1 or 2 characters to display. Automatically uppercased. Single char = larger font (48% of size), two chars = 38%.
sizenumber48Diameter of the circle in pixels.
colorAvatarColor'orange'Background color. Accepts a preset key (orange, indigo, pink, emerald, amber, sky, rose, violet) or any CSS color string.
gradient[string, string] | nullundefinedTwo-stop gradient colors. When provided, overrides solid color. Rendered as a LinearGradient (top-left to bottom-right).
levelnumberundefinedAuteur level 1–3. Renders a colored circle badge at bottom-right. Values outside 1–3 are ignored.
Usage
// Standard avatar with gradient and level badge <Avatar label="RG" size={48} gradient={['#ea580c', '#dc2626']} level={2} /> // Small single-letter avatar for nav <Avatar label="R" size={22} color="orange" />
Toast components/Toast.tsx

Slide-in notification anchored to the bottom-right of the screen. 300px wide, slides in from the right edge, pauses for reading, then slides back out. Supports four semantic variants with corresponding icons and color themes.

Props
PropTypeDefaultDescription
visiblerequiredbooleanControls whether the toast is shown. Triggers slide-in when changed to true.
messagerequiredstringThe notification text. Capped at 2 lines.
onDismissrequired() => voidCalled after the auto-dismiss animation completes.
variant'success' | 'info' | 'warning' | 'error''success'Semantic variant controlling icon (Check/Info/AlertTriangle/X) and color theme.
iconLucideIconvariant defaultOverride the default variant icon with any Lucide icon component.
durationnumber4000Milliseconds the toast stays visible before sliding out.
Usage
const [show, setShow] = useState(false); <Toast visible={show} message="Story created successfully!" variant="success" onDismiss={() => setShow(false)} /> <Toast visible={errorVisible} message="Insufficient tokens" variant="error" duration={6000} onDismiss={() => setErrorVisible(false)} />
TokenBalanceChip components/TokenBalanceChip.tsx

Compact chip showing the user's live token balance with an odometer-style rolling digit animation. Tapping opens a fullscreen cinematic modal with a video background. Two video states based on balance: deficit (<100 tokens) shows the film crew urging the user to earn; surplus (≥100) shows a celebration. Balance changes trigger a pulse animation and floating delta indicator.

Props
PropTypeDefaultDescription
refreshKeynumber0Increment this value to trigger a token balance refresh from the server.
onPress() => voidundefinedOverride the built-in modal with a custom press handler.
compactbooleanfalseCompact display mode for tight layout spaces.
Usage
// In TopNav trailing slot — refreshes when story completes <TokenBalanceChip refreshKey={storyCompletedCount} /> // With custom press override <TokenBalanceChip onPress={() => navigation.navigate('TokenStore')} />

Layout Components

AppShell components/layout/AppShell.tsx

Standard authenticated-screen chrome providing a fixed header, scrollable content area constrained to 1200px max-width, optional footer, and optional floating action button. The outer viewport uses a dark zinc gradient. Content and footer slots accept any React node, enabling composition with TopNav, Footer, and other layout components.

Props
PropTypeDefaultDescription
headerrequiredReactNodeHeader component fixed above scroll content. Typically <TopNav />.
childrenrequiredReactNodeMain scrollable content rendered inside the content area.
footerReactNodeundefinedOptional footer pinned below the scroll content. Typically <Footer />.
fabReactNodeundefinedFloating action button positioned at bottom-right of the content area.
contentPaddingnumber16Padding applied to the scrollable content area in pixels.
scrollEnabledbooleantrueSet to false for screens using FlatList or other self-scrolling content.
onRefresh() => voidundefinedPull-to-refresh callback. No-op on web.
refreshingbooleanfalseWhether the refresh indicator is visible.
backgroundColorstringzinc gradientOverride the outer viewport background color.
Usage
<AppShell header={ <TopNav leading="logo" links={navLinks} linksPosition="inline" /> } footer={<Footer />} onRefresh={handleRefresh} refreshing={isRefreshing} > <HomeHero user={user} /> </AppShell>
TopNav components/layout/TopNav.tsx

Material Design 3 top app bar with leading action, optional nav links (inline or below), trailing icon buttons, avatar pill, token chip, and an animated traveling bottom border. Adapts between mobile app nav and website nav patterns via linksPosition.

Props
PropTypeDefaultDescription
titlestringundefinedScreen title rendered as Appbar.Content.
leading'menu' | 'back' | 'close' | 'logo' | ReactNodeundefinedLeft side content. String values render icon buttons; 'logo' renders the Di Gusto Logo component; ReactNode renders as-is.
trailingArray<'search' | 'more' | ReactNode>[]Right side custom items rendered before the built-in icon buttons.
linksTopNavLink[]undefinedNavigation links. Each has label, optional icon, onPress, and active state.
linksPosition'below' | 'inline''below'inline places links in the main header row (website mode). below renders a second row beneath the header (mobile mode).
elevatedbooleantrueShow drop shadow below the nav bar.
backgroundColorstringtheme surfaceOverride nav background color.
animatedBorderbooleanfalseRender an animated gradient highlight that travels left-to-right along the bottom edge.
leadingExtraReactNodeundefinedExtra content rendered immediately after the logo/leading on the left side.
showControlsbooleanfalseShow controls (SlidersHorizontal) icon in trailing area.
showNotificationsbooleanfalseShow recipes (ChefHat) icon in trailing area.
showStorebooleanfalseShow store (ShoppingBag) icon in trailing area.
showMapbooleanfalseShow journey map (Map) icon in trailing area.
showMenubooleanfalseShow hamburger menu icon in trailing area.
showAvatarbooleanfalseShow avatar pill (level title + Avatar) in the trailing area.
avatarLabelstringundefined1–2 character initials for the avatar.
avatarLevelnumberundefinedAuteur level (1–3) for the avatar badge color.
avatarLevelTitlestringundefinedLevel title label (e.g. "Director") shown in the avatar pill.
avatarGradient[string, string]undefinedGradient for the avatar background.
tokenChipReactNodeundefinedToken balance chip rendered adjacent to the avatar pill. Pass <TokenBalanceChip />.
activeSection'recipes' | 'store'undefinedHighlights the corresponding icon with the primary color.
showIconLabelsbooleanfalseShow text labels below icon buttons.
onLeadingPress() => voidnavigate to StoryEntryOverride the leading icon press handler.
onMenuPress() => voidundefinedCalled when the hamburger menu icon is pressed.
onAvatarPress() => voidundefinedCalled when the avatar pill is pressed.
onControlsPress() => voidundefinedCalled when controls icon is pressed.
onStorePress() => voidundefinedCalled when store icon is pressed.
onMapPress() => voidundefinedCalled when map icon is pressed.
Usage
<TopNav leading="logo" links={[ { label: 'Play', onPress: () => {}, active: true }, { label: 'Recipes', onPress: () => {} }, ]} linksPosition="inline" showAvatar avatarLabel="RG" avatarLevel={2} avatarLevelTitle="Director" tokenChip={<TokenBalanceChip />} showMenu onMenuPress={openNavMenu} animatedBorder />
ModalShell components/layout/ModalShell.tsx

Full-screen modal with animated backdrop, optional header (title + close button), scrollable content, and optional footer. Supports ESC to close on web, backdrop-press to close, and configurable animation duration. Used as the foundation for all modals in the app including NavMenu (desktop) and AuteurLevelModal.

Props
PropTypeDefaultDescription
visiblerequiredbooleanControls modal visibility. Triggers fade animation.
onCloserequired() => voidCalled when the modal should close (close button, backdrop press, or ESC key).
childrenrequiredReactNodeMain modal content rendered in the scrollable area.
titlestringundefinedOptional title shown in the modal header.
footerReactNodeundefinedOptional content pinned to the bottom of the modal.
showCloseButtonbooleantrueShow the close (×) button in the header.
closeOnBackdropbooleantrueClose modal when the backdrop is pressed.
closeOnEscbooleantrueClose on ESC key press (web only).
scrollEnabledbooleantrueSet false for non-scrolling content (e.g. FlatList).
contentPaddingnumber24Padding applied to the scrollable content container.
backgroundColorstringtheme.colors.backgroundOverride the modal container background color.
animationDurationnumber250Fade animation duration in milliseconds.
Usage
<ModalShell visible={showSettings} onClose={() => setShowSettings(false)} title="Settings" footer={<SaveButton />} > <SettingsForm /> </ModalShell>
SplitScreen components/layout/SplitScreen.tsx

Two-panel responsive layout: content panel + media panel. Side-by-side on wide screens (≥768px), stacked on narrow with media on top. Media panel supports image, video, solid color, gradient, or a playlist of videos with smooth CrossfadeVideo transitions. Optional background audio with mute toggle, crossfade between playlist tracks.

Props
PropTypeDefaultDescription
childrenrequiredReactNodeContent rendered in the content panel.
mediarequiredSplitScreenMedia | SplitScreenMedia[]Media configuration. Pass an array for a playlist. Types: image, video, color, gradient.
contentSide'left' | 'right''left'Which side content appears on in wide mode. In narrow mode media is always on top.
mediaShuffleStartbooleantrueStart playlist at a random index.
breakpointnumber768Viewport width (px) at which layout switches from stacked to side-by-side.
ratio[number, number][1, 1]Flex ratio [content, media] in wide mode.
mediaOverlay{ color?: string; opacity?: number }undefinedSemi-transparent overlay on the media panel for contrast/darkening.
mediaContentReactNode | (media, index) => ReactNodeundefinedContent overlaid on top of the media panel (branding, captions). Centered and auto-sized.
contentPaddingnumber0Padding for the content panel. Let children handle their own padding by default.
showMediaOnNarrowbooleantrueShow media panel on narrow/mobile screens.
narrowMediaRationumber0.4Media panel flex ratio relative to content in narrow mode.
audioSplitScreenAudio | SplitScreenAudio[]undefinedBackground audio. Starts muted with a toggle icon in the media panel corner. Crossfades between playlist tracks.
onVideoEnd() => voidundefinedCalled when a non-looping video finishes (fires per video in playlists before auto-advance).
Usage
<SplitScreen contentSide="left" media={{ type: 'video', source: 'https://cdn.digusto.ai/intro.mp4' }} mediaOverlay={{ opacity: 0.35 }} mediaContent={<Logo size="xl" variant="light" tagline />} audio={{ source: 'https://cdn.digusto.ai/ambient.mp3', volume: 0.3 }} > <LoginForm /> </SplitScreen>

Common Components

Tooltip components/common/Tooltip.tsx

Accessible tooltip wrapper that shows a label on hover (web) or long-press (native). Wraps any pressable child and displays the tooltip label in a small dark popover above or below the target element.

Props
PropTypeDefaultDescription
titlerequiredstringTooltip label text displayed on hover/long-press.
childrenrequiredReactNodeThe element that triggers the tooltip.
Usage
<Tooltip title="Game currency"> <TokenBalanceChip /> </Tooltip>
SideLabel components/common/SideLabel.tsx

Vertically-oriented sidebar label used to annotate sections of the UI with rotated text. Typically used in split layouts to label the media panel or content zones.

Props
PropTypeDefaultDescription
labelrequiredstringText to display in the vertical sidebar label.
side'left' | 'right''left'Which side of the container the label is anchored to.
Usage
<SideLabel label="SCENE 01" side="left" />
VarietySpinner components/common/VarietySpinner.tsx

Loading spinner that cycles through multiple visual styles ("variety") to keep long-loading states visually interesting. Wraps React Native's ActivityIndicator with Di Gusto brand colors.

Props
PropTypeDefaultDescription
size'small' | 'large' | number'large'Spinner size.
colorstringtheme primarySpinner color override.
styleViewStyleundefinedAdditional container styles.
Usage
{isLoading && <VarietySpinner size="large" />}

Story Components

SceneLabel components/story/SceneLabel.tsx

Cinematic scene identifier label displaying act/scene numbers and a custom label. Supports a glass-morphism variant for use over video backgrounds. Optionally accepts children for additional descriptive text below the primary label.

Props
PropTypeDefaultDescription
scenerequirednumberScene number.
actrequirednumberAct number.
labelrequiredstringPrimary label text (e.g. "★ 1,250 TOKENS").
variant'default' | 'glass''default'glass uses backdrop-blur for overlay on video/images.
childrenReactNodeundefinedAdditional descriptive content rendered below the label.
Usage
<SceneLabel scene={1} act={1} label="★ 1,250 TOKENS" variant="glass" > <Text>Earn tokens by voting on stories.</Text> </SceneLabel>
StoryBadge components/story/StoryBadge.tsx

Badge component displaying story and act metadata. Used to mark story cards, scene headers, and timeline entries with their Act/Scene identifier in the Di Gusto cinematic style.

Props
PropTypeDefaultDescription
actnumber1Act number displayed in the badge.
scenenumber1Scene number displayed in the badge.
variant'default' | 'compact''default'Size variant. compact for tight card contexts.
styleViewStyleundefinedAdditional container styles.
Usage
<StoryBadge act={2} scene={3} />
VocabularyCard components/story/VocabularyCard.tsx

Italian vocabulary flashcard displayed during story transitions or as interstitials. Shows the Italian word prominently with pronunciation guide and English translation. Styled with warm gold tones to match the Di Gusto aesthetic.

Props
PropTypeDefaultDescription
wordrequiredstringItalian vocabulary word.
pronunciationstringundefinedPhonetic pronunciation guide.
translationrequiredstringEnglish translation.
contextstringundefinedStory context or example sentence using the word.
styleViewStyleundefinedAdditional container styles.
Usage
<VocabularyCard word="Buongiorno" pronunciation="bwon-JOR-no" translation="Good morning" context="Used as a morning greeting until around noon." />
IngredientSpotlightCard components/story/IngredientSpotlightCard.tsx

Card component that highlights a featured ingredient from the current story. Shows ingredient name, origin, flavor profile, and a brief culinary description. Used in story scenes where food culture intersects with the narrative.

Props
PropTypeDefaultDescription
ingredientrequiredstringIngredient name.
originstringundefinedGeographic origin (e.g. "Tuscany, Italy").
descriptionrequiredstringCulinary description and flavor notes.
imageUrlstringundefinedOptional hero image URL for the ingredient.
styleViewStyleundefinedAdditional container styles.
Usage
<IngredientSpotlightCard ingredient="Parmigiano Reggiano" origin="Emilia-Romagna, Italy" description="Aged 24 months, with a crystalline texture and nutty, complex flavor." imageUrl="https://cdn.digusto.ai/ingredients/parmigiano.jpg" />
BePatient components/story/BePatient.tsx

Patience / loading interstitial shown while AI story generation is in progress. Displays an animated waiting state with Italian-themed copy and a progress indicator. Keeps users engaged during the 15–30 second story generation window.

Props
PropTypeDefaultDescription
messagestring'Creating your story…'Loading message displayed below the animation.
progressnumberundefinedOptional progress value 0–1 for a progress bar.
styleViewStyleundefinedAdditional container styles.
Usage
{isGenerating && ( <BePatient message="Weaving your Italian adventure…" progress={generationProgress} /> )}
ItalianPatience components/story/ItalianPatience.tsx

Italian-themed patience screen — a more cinematic variant of BePatient featuring Italian idioms, scenic imagery, and character-driven copy. Used for longer wait states where a richer experience is appropriate (e.g. first story generation).

Props
PropTypeDefaultDescription
onReady() => voidundefinedCalled when the patience screen determines the user is ready to proceed.
minDisplayMsnumber3000Minimum time in ms to display before calling onReady.
styleViewStyleundefinedAdditional container styles.
Usage
<ItalianPatience minDisplayMs={4000} onReady={proceedToStory} />

Media Components

SmartVideo components/SmartVideo.tsx

Smart HTML video player with viewport detection — pauses when scrolled out of view and resumes when back in view (IntersectionObserver on web). Supports autoplay, mute, loop, object-fit, and an onEnded callback.

Props
PropTypeDefaultDescription
srcrequiredstringVideo source URL.
autoPlaybooleanfalseStart playing immediately when in viewport.
mutedbooleantrueMute the video (required for autoplay in most browsers).
loopbooleanfalseLoop the video playback.
objectFit'cover' | 'contain' | 'fill''cover'CSS object-fit for the video element.
styleViewStyleundefinedStyles applied to the video container.
onEnded() => voidundefinedCalled when video playback ends.
Usage
<SmartVideo src="https://cdn.digusto.ai/videos/hero.mp4" autoPlay muted loop objectFit="cover" style={styles.videoBackground} onEnded={handleVideoEnd} />
CrossfadeVideo components/CrossfadeVideo.tsx

Video player with smooth crossfade transitions between a playlist of clips. Preloads the next clip for seamless transitions. Used internally by SplitScreen for video playlists and can be used standalone for story scene sequences.

Props
PropTypeDefaultDescription
scenesrequiredCrossfadeScene[]Array of scenes with id, url, and optional posterUrl.
startIndexnumber0Index of the first scene to play.
mutedbooleantrueMute all video clips.
loopbooleantrueLoop the entire playlist after the last scene.
viewportbooleantruePause when scrolled out of viewport.
crossfadeDurationnumber1000Crossfade transition duration in ms.
onSceneChange(index: number) => voidundefinedCalled when the active scene changes.
Usage
<CrossfadeVideo scenes={[ { id: 's1', url: 'https://cdn.digusto.ai/scene1.mp4' }, { id: 's2', url: 'https://cdn.digusto.ai/scene2.mp4' }, ]} muted loop crossfadeDuration={1500} onSceneChange={(i) => setCurrentScene(i)} />
VideoModal components/VideoModal.tsx

Fullscreen video modal overlay. Tapping the backdrop or pressing ESC dismisses the modal. Used for previewing story clips, showcasing ingredients, and cinematic reveals.

Props
PropTypeDefaultDescription
visiblerequiredbooleanControls modal visibility.
srcrequiredstringVideo source URL.
onCloserequired() => voidCalled when the modal is dismissed.
autoPlaybooleantrueStart playing when modal opens.
posterstringundefinedPoster image shown before video loads.
Usage
<VideoModal visible={showClip} src="https://cdn.digusto.ai/clips/scene-preview.mp4" poster="https://cdn.digusto.ai/clips/scene-preview-poster.jpg" onClose={() => setShowClip(false)} />

Modal Components

CreditPurchaseModal components/CreditPurchaseModal.tsx

In-app purchase modal for acquiring token credits. Displays current balance, available token pack options with pricing, and handles the purchase flow. Called from TokenBalanceChip when the user needs more tokens to start a story.

Props
PropTypeDefaultDescription
visiblerequiredbooleanControls modal visibility.
onDismissrequired() => voidCalled when the modal is dismissed without purchase.
currentBalancerequirednumberUser's current token balance displayed in the modal header.
onPurchaseComplete() => voidundefinedCalled after a successful purchase completes.
Usage
<CreditPurchaseModal visible={showPurchase} onDismiss={() => setShowPurchase(false)} currentBalance={balance} onPurchaseComplete={handlePurchaseDone} />
AuteurLevelModal components/AuteurLevelModal.tsx

Modal showing the user's current auteur/creator level progression — Audience (1), Director (2), or Auteur (3) — along with token stats, lifetime earnings, and what's unlocked at each tier. Opened from the avatar pill in TopNav.

Props
PropTypeDefaultDescription
visiblerequiredbooleanControls modal visibility.
onCloserequired() => voidCalled when the modal is dismissed.
level1 | 2 | 31Current auteur level.
tokenBalancenumber0Current token balance shown in the stats section.
lifetimeTokensnumber0Lifetime tokens earned shown in the stats section.
Usage
<AuteurLevelModal visible={showLevel} onClose={() => setShowLevel(false)} level={profile.authority_level} tokenBalance={balance} lifetimeTokens={profile.lifetime_tokens} />

Auth Components

AuthGuard components/auth/AuthGuard.tsx

Authentication gate wrapper that conditionally renders children only when the user is authenticated. Redirects unauthenticated users to the sign-in screen. Optionally accepts a fallback component to show while the auth state is loading.

Props
PropTypeDefaultDescription
childrenrequiredReactNodeContent to render when authenticated.
fallbackReactNode<VarietySpinner />Content to render while auth state is loading.
redirectTostring'SignIn'Screen name to navigate to if not authenticated.
Usage
// Wrap protected screens <AuthGuard fallback={<ItalianPatience />} > <StoryEntryScreen /> </AuthGuard>
ErrorBoundary components/ErrorBoundary.tsx

React class component error boundary that catches rendering errors in the component tree below it. Renders a fallback UI instead of crashing the entire app. Logs errors to the console and optionally to external error tracking.

Props
PropTypeDefaultDescription
childrenrequiredReactNodeComponent tree to wrap with error boundary protection.
fallbackReactNodeerror message UIFallback UI to render when an error is caught.
onError(error: Error, info: ErrorInfo) => voidundefinedCalled when an error is caught, useful for error reporting services.
Usage
<ErrorBoundary fallback={<ErrorScreen onRetry={resetApp} />} onError={(err) => Sentry.captureException(err)} > <AppNavigator /> </ErrorBoundary>