src/components. Import the kit from the barrel:
useColors() and the accent from useBrandPalette(), and lays out rows with rtlRow. Build screens from these instead of hand-rolling buttons, inputs or rows. A hand-rolled control will not follow the theme, the studio color or the layout direction.
Most icon props take an Ionicons glyph name from @expo/vector-icons.
Layout
Screen
The page wrapper. Handles safe area, background, skin backdrop, scrolling, pull to refresh, the keyboard and a pinned footer. It has its own page: Keyboard handling and Screen.Row
AView with flexDirection: rtlRow.
Card
A themed surface.SectionHeader
AtitleMd heading with an optional trailing link.
BrandTopBar
The header for main screens. The title leads and the studio logo closes the row, so both mirror with the language.
The logo is 132 by 48. On a load error it falls back to the studio name in the brand color.
AmbientGlow
Two soft brand-colored orbs behind a classic screen.Screen renders it by default when no skin is active.
Gradient ids are made unique per mount with
useId, because tab screens stay mounted together.
Text
AppText
All text goes throughAppText.
LinkText
Tappablelabel text with accessibilityRole="link".
Badge
A small pill with optional icon.Buttons and pressables
Button
filled and tonal use the studio brand color. whatsapp uses fixed WhatsApp green. Press feedback is a scale to 0.97 that respects the system reduce-motion setting.
IconButton
A round icon-only button.
Always pass
accessibilityLabel.
BackButton
AGlassIconButton with the direction-aware back arrow.
PressableScale
The base for custom tappable surfaces. APressable that scales down on press.
Reanimated does not apply function-form
style props on animated components. Use pressedStyle instead of style={({ pressed }) => ...}.Chip
A selectable pill.ActionRow and ActionCard
A navigation row: icon tile, title, optional subtitle, and a forward chevron that follows the reading direction.ActionCard is the same row inside a Card.
PlanSwitcher
A horizontal row of plan chips used by the workouts and nutrition tabs when a trainee has more than one plan.
A plan with
kind: 'file' shows a badge. filePlanSource(pdfUrl) decides between the link badge and the file badge.
Inputs
TextField
ATextInput with label, error and optional leading icon. Forwards its ref.
Text alignment uses
surfaceStart and surfaceEnd, because TextInput.textAlign is a prop and React Native does not mirror props. See RTL and language.
SearchField
TextField with a search icon, returnKeyType="search", no autocorrect and no autocapitalize. Pair it with foldForSearch, searchTerms and matchesSearchTerms from src/lib/search.ts for Hebrew-aware matching: those fold niqqud, final letter forms, accents and punctuation before comparing.
PasswordField
A secureTextInput with a show and hide toggle. Not used by the trainee sign in, which has no passwords.
OtpInput
Six cells drawn over one hidden input.
It sets
textContentType="oneTimeCode" and autoComplete="sms-otp" for system autofill.
Progress and loading
ProgressBar
The glow uses
boxShadow. React Native only draws outset box shadows on Android from API 28, and the app’s minSdkVersion is 26, so on older Android the bar draws three concentric translucent layers instead.
Ring
A circular progress ring drawn withreact-native-svg.
Skeleton
Pulsing placeholders. Four exports:Confetti
Confetti takes a numeric trigger. It renders nothing while the value is 0 or less, and replays each time the number changes. Sixteen pieces fall for 1500 ms in the brand color plus four fixed colors.
Feedback
ToastProvider and useToast
ToastProvider is mounted once in the root layout. Anywhere below it:
'success' (default) or 'error'. A toast shows for 2200 ms at the top of the screen. A new toast replaces the current one.
Icons
Icon
Renders an SVG fromassets/icons by name.
iconRegistry.ts imports each SVG as a component through react-native-svg-transformer, which is wired in metro.config.js. IconName is the union of registry keys. src/types/svg.d.ts declares the module type.
The kit mixes two icon sources. Icon covers the custom set. Most components take Ionicons names directly. lucide-react-native is also a dependency.
Tabs
AndroidTabsLayout and AndroidTabBar build the Android tab bar: a floating glass dock, at most 560 wide, with a haptic tick on change and tabBarHideOnKeyboard. iOS uses native tabs instead. See Route tree.
Glass primitives
src/components/glass wraps expo-glass-effect. LIQUID_GLASS in glass/utils.ts is computed once from liquidGlassAvailable() in src/lib/liquidGlass.ts, which requires both isLiquidGlassAvailable() and isGlassEffectAPIAvailable() and returns false if either throws.
GlassSurface props: radius, tint, interactive, native (set false to force the fallback), glassStyle, fallbackIntensity (default 28), borderWidth.
GlassIconButton props: icon, accessibilityLabel, onPress, onLongPress, iconColor, size (44), iconSize (22), active, solid.
Rules carried over from mobile/docs/liquid-glass.md that the code still follows:
- Do not put
backgroundColoron a glass view.splitTint()lifts it out of the style and passes it as the nativetintColor. - Inside sheets and modals native glass does not composite. Pass
solidtoGlassIconButtonso it renders an opaquesheetCardSurfacebutton. activerenders a solid brand-colored button.
App-level components
These sit next to the kit insrc/components.