Skip to content

Component Architecture

Shaker’s component architecture follows React best practices with a focus on reusability, type safety, and the RetroUI design system. Components are organized by function and use NativeWind for styling with design tokens.

components/
├── BarcodeScanner.tsx # Camera-based barcode scanner
├── BatchScanQueue.tsx # Batch scan queue management
├── BudgetAlerts.tsx # Budget threshold alerts
├── CostAnalytics.tsx # Cost analytics display
├── ErrorBoundary.tsx # Error boundary wrapper
├── ErrorState.tsx # Error state display
├── EventCard.tsx # Event display card
├── EventForm.tsx # Event create/edit form
├── EventHosts.tsx # Event hosts management
├── EventList.tsx # Event list display
├── FlavorProfileChart.tsx # Flavor radar chart
├── FlavorProfileEditor.tsx # Interactive flavor profile editor
├── GuestCard.tsx # Guest display card
├── IngredientMappingSuggestions.tsx # Ingredient mapping suggestions
├── IngredientPicker.tsx # Single ingredient selector
├── IngredientSelector.tsx # Multi-ingredient selector
├── IngredientSubstitutionSuggestions.tsx # Ingredient substitution suggestions
├── Input.tsx # Text input
├── LoadingScreen.tsx # Full-screen loading state
├── Modal.tsx # Modal dialog
├── PriceHistoryChart.tsx # Price history chart
├── ProductLevelIndicator.tsx # Product inventory level display
├── ProductScannerDemo.tsx # Barcode scanner demo
├── ProductSelectionModal.tsx # Product picker modal
├── QuickUpdateModal.tsx # Quick inventory update modal
├── ReplyMessageBar.tsx # Message reply bar
├── RestockModal.tsx # Restock quantity modal
├── RootComponent.tsx # App root wrapper
├── Screen.tsx # Full-screen wrapper
├── SquareButton.tsx # Square action button
├── StatCard.tsx # Dashboard stat card
├── StyledImage.tsx # Styled Expo image wrapper (exported as StyledExpoImage)
├── SubstitutionSuggestions.tsx # Product substitution suggestions
├── TagSelector.tsx # Tag selection input
├── TextInput.tsx # Enhanced text input
├── Toast.tsx # Toast notifications (Toast + ToastContainer)
├── batch/ # Batch recipe components
│ ├── BatchIngredientList.tsx # Scaled ingredient list display
│ ├── BatchScalingCalculator.tsx # Serving size calculator
│ └── BatchServingTracker.tsx # Batch serving tracker
├── icons/ # Icon configuration
│ └── FontAwesomeSetup.tsx # FontAwesome Pro library setup
├── ingredients/ # Ingredient-specific components
│ └── IngredientFilters.tsx # Ingredient filtering controls
├── navigation/ # Navigation components
│ └── HeaderButton.tsx # Header buttons (BaseHeaderButton, HeaderBackButton, HeaderActionButton, headerButtonConfig)
├── products/ # Product-specific components
│ ├── ProductFilters.tsx # Product filtering controls
│ ├── ProductInstanceCard.tsx # Product instance display card
│ ├── ProductInstanceList.tsx # Product instance list
│ ├── QuantityControl.tsx # Quantity increment/decrement control
│ └── TrackingModeBadge.tsx # Tracking mode indicator badge
├── recipes/ # Recipe-specific components
│ ├── RecipeCard.tsx # Recipe display card
│ └── RecipeFilters.tsx # Recipe filtering controls
├── skeletons/ # Loading skeleton components
│ └── ProductCardSkeleton.tsx # Product card + list skeleton loaders
└── tags/ # Tag components
├── TagBadge.tsx # Tag badge display
├── TagFilter.tsx # Tag filter input
├── TagList.tsx # Tag list display
└── TagPicker.tsx # Tag picker modal

The following components are sourced from the dry_ui design system library and re-exported via components/index.ts. They are not local files:

ComponentDescription
AlertAlert banner
BadgeLabel/tag
ButtonPrimary action button
CardCard container
DateTimePickerDate/time picker
EmptyStateEmpty list state
IconFontAwesome icon wrapper
ImageStyled Expo image
PickerDropdown picker
ScreenHeadingScreen title
SegmentedControlSegmented control
SliderRange slider
SwipeableRowSwipeable list item
TextStyled text
TouchableOpacityStyled touchable
VerticalSpacerVertical spacing utility
ViewStyled view

Full-screen wrapper with consistent styling and safe area handling.

Usage:

import { Screen } from '@/components';
export default function MyScreen() {
return (
<Screen>
<Text>Screen content</Text>
</Screen>
);
}

Props:

  • children: ReactNode - Screen content
  • className?: string - Additional NativeWind classes
  • scrollable?: boolean - Enable ScrollView (default: true)

From dry_ui design system. Re-exported via @/components.

Container with NeoBrutalism styling (bold border, shadow, padding).

Usage:

import { Card } from '@/components';
<Card>
<Text>Card content</Text>
</Card>

Props:

  • children: ReactNode - Card content
  • className?: string - Additional classes
  • onPress?: () => void - Make card pressable

From dry_ui design system. Re-exported via @/components.

Primary action button with NeoBrutalism styling.

Usage:

import { Button } from '@/components';
<Button
title="Add Recipe"
onPress={handlePress}
variant="primary"
/>

Props:

  • title: string - Button text
  • onPress: () => void - Press handler
  • variant?: 'primary' | 'secondary' | 'danger' - Style variant
  • disabled?: boolean - Disabled state
  • loading?: boolean - Loading state

Square action button for dashboard quick actions.

Usage:

import { SquareButton } from '@/components';
<SquareButton
icon="plus"
label="Add Recipe"
onPress={handlePress}
color="primary"
/>

Props:

  • icon: IconName - FontAwesome icon name
  • label: string - Button label
  • onPress: () => void - Press handler
  • color?: ColorToken - Background color from design tokens

From dry_ui design system. Re-exported via @/components.

FontAwesome icon wrapper with design system integration.

Usage:

import { Icon } from '@/components';
<Icon
name="whiskey-glass"
size="iconLg"
color="primary"
/>

Props:

  • name: IconName - FontAwesome icon name
  • size?: IconSize - Size from design tokens
  • color?: ColorToken - Color from design tokens

Icon Sizes:

  • iconXs: 12px
  • iconSm: 16px
  • iconMd: 20px (default)
  • iconLg: 24px
  • iconXl: 32px
  • icon2xl: 48px
  • iconBar: 24px (tab bar icons)

Camera-based barcode scanner for UPC lookup.

Usage:

import { BarcodeScanner } from '@/components';
<BarcodeScanner
onScan={handleBarcodeScan}
onClose={handleClose}
/>

Props:

  • onScan: (barcode: string) => void - Scan callback
  • onClose: () => void - Close callback
  • isVisible: boolean - Visibility state

Features:

  • Camera permission handling
  • Barcode format detection
  • Visual scan feedback
  • Error handling

Visual indicator for product inventory level.

Usage:

import { ProductLevelIndicator } from '@/components';
<ProductLevelIndicator
level={75}
size="md"
variant="horizontal"
/>

Props:

  • level: number - Percentage (0-100)
  • size?: 'sm' | 'md' | 'lg' - Display size
  • variant?: 'horizontal' | 'vertical' - Orientation
  • showLabel?: boolean - Show percentage label

Color Coding:

  • 75-100%: Green (good stock)
  • 25-74%: Yellow (medium stock)
  • 0-24%: Red (low stock)

Radar/spider chart displaying an ingredient’s 8-dimension flavor profile.

Usage:

import { FlavorProfileChart } from '@/components';
<FlavorProfileChart
profile={{
sweet: 7,
sour: 3,
bitter: 2,
umami: 1,
spicy: 0,
herbal: 4,
floral: 2,
fruity: 6
}}
title="Flavor Profile"
size="medium"
showLabels
showValues
/>

Props:

  • profile: IngredientProfile - 8-dimensional flavor data
  • title?: string - Chart title (default: "Flavor Profile")
  • size?: 'small' | 'medium' | 'large' - Chart size (default: 'medium')
  • showLabels?: boolean - Show flavor axis labels (default: true)
  • showValues?: boolean - Show values on tap (default: true)
  • className?: string - Additional NativeWind classes

Interactive editor for flavor profiles.

Usage:

import { FlavorProfileEditor } from '@/components';
<FlavorProfileEditor
profile={currentProfile}
onChange={handleProfileChange}
/>

Props:

  • profile: IngredientProfile - Current profile
  • onChange: (profile: IngredientProfile) => void - Change callback

Features:

  • Slider controls for each dimension (0-10)
  • Real-time preview
  • Validation
  • Reset functionality

Components use composition for flexibility:

<Card>
<Card.Header>
<Text>Title</Text>
</Card.Header>
<Card.Content>
<Text>Content</Text>
</Card.Content>
<Card.Footer>
<Button title="Action" />
</Card.Footer>
</Card>

For flexible rendering:

<List
data={items}
renderItem={({ item }) => (
<Card>
<Text>{item.name}</Text>
</Card>
)}
/>

Form inputs are controlled:

const [value, setValue] = useState('');
<Input
value={value}
onChangeText={setValue}
placeholder="Enter name"
/>

Components use design tokens for consistency:

import { designTokens } from '@/styles/designTokens';
<View style={{
backgroundColor: designTokens.colors.teal.DEFAULT,
borderWidth: designTokens.borders.thick,
borderColor: designTokens.colors.black,
padding: designTokens.spacing.md,
}} />

Prefer NativeWind classes for styling:

<View className="bg-teal border-3 border-black p-4 rounded-lg shadow-retro-md">
<Text className="text-xl font-bold text-black">
Title
</Text>
</View>

Use tailwind-variants for component variants:

import { tv } from 'tailwind-variants';
const buttonStyles = tv({
base: 'px-4 py-2 rounded-lg border-3 border-black',
variants: {
color: {
primary: 'bg-primary',
secondary: 'bg-secondary',
danger: 'bg-error',
},
},
});

2026-03-06