Shared building blocks live in src/components. Import the kit from the barrel:
Every primitive reads neutrals from 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

A View with flexDirection: rtlRow.

Card

A themed surface.

SectionHeader

A titleMd 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 through AppText.

LinkText

Tappable label 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

A GlassIconButton with the direction-aware back arrow.

PressableScale

The base for custom tappable surfaces. A Pressable 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

A TextInput 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 secure TextInput 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 with react-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:
The variant is '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 from assets/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 backgroundColor on a glass view. splitTint() lifts it out of the style and passes it as the native tintColor.
  • Inside sheets and modals native glass does not composite. Pass solid to GlassIconButton so it renders an opaque sheetCardSurface button.
  • active renders a solid brand-colored button.

App-level components

These sit next to the kit in src/components.