JustToThePoint English Website Version
JustToThePoint en español

App-wide Styling

Assumption is the mother of all screw-ups.

If you choose not to decide, you still have made a choice, Neil Peart

alt

The Core Problem We’re Solving

Imagine you’re building an app and you want a specific shade of gold for certain elements - maybe buttons, icons, or highlights. Right now, you probably have something like this scattered across different files:

This creates several problems:

  1. Inconsistency risk - If you change gold in one file but forget another, colors don’t match.
  2. Maintenance nightmare - Want to change gold? You have to find and edit every single file
  3. No single source of truth - There’s no one place to see all your app’s visual constants

Step 1. What We’re Building: A Central Theme File

The solution is to create ONE file (src/theme.ts) that contains all your colors, spacing values, and typography settings. Every other file then imports from this single source.

Think of it like a company brand guide - instead of each department making up their own logo colors, everyone refers to the same official brand book.

Understanding the Two Layout Files

This is crucial and often confuses beginners:

  1. Root Layout: app/_layout.tsx.
    This is the parent container for your ENTIRE app
    It wraps everything, including all screens and the tab navigator
    Contains important setup like SQLiteProvider (your database)
    Think of it as the foundation of a house

  2. Tab Layout: app/(tabs)/_layout.tsx
    This is inside the root layout
    It only handles the bottom tab bar navigation
    Contains Tabs and Tabs.Screen definitions
    Think of it as configuring just the navigation bar at the bottom

The Import Path Difference

This trips up many beginners because it relates to folder structure:

  1. For app/_layout.tsx importing theme:
    Current location: app/ folder
    Theme location: src/ folder
    To get from app/ to src/, you go UP one level first (../)
    So: ../src/theme

  2. For app/(tabs)/_layout.tsx importing theme:
    Current location: app/(tabs)/ folder (two levels deep)
    Theme location: src/ folder
    To get from app/(tabs)/ to src/, you go UP two levels first (../../)
    So: ../../src/theme
    Think of ../ as “go up one folder level.” The more folders deep you are, the more ../ you need.

app/
├── _layout.tsx           ← ROOT layout  (wraps entire app)
│                              Contains: Suspense, SQLiteProvider, Stack
│                              Theme import: "../src/theme"     ← ONE level up
│
└── (tabs)/
    ├── _layout.tsx       ← TAB layout   (configures tab bar only)
    │                          Contains: Tabs, Tabs.Screen entries
    │                          Theme import: "../../src/theme"  ← TWO levels up
    │
    ├── verse.tsx
    ├── videos.tsx
    ├── online.tsx
    ├── prayers.tsx
    └── diary.tsx

The Test Moment: Why This Matters

The test at the end is elegant. We change ONE color in ONE file:

gold: "#c9a84c", // ← change to "#e5a832"

Common Pitfalls to Avoid

  1. Don’t mix up the layout files - The root layout has SQLiteProvider. If you accidentally put tab bar code there (or remove the SQLiteProvider), your database won’t work and you’ll get errors.
  2. Import path errors - Getting ../ vs ../../ wrong is super common. If you get an error like “Cannot find module,” your path is probably wrong.
  3. Forgetting to remove old constants - After creating the theme file, make sure to delete all those scattered const C = {…} objects from your individual files.

What is a Theme File?

Think of a theme file (e.g., src/theme.ts) as a design rulebook for your entire app. Instead of typing the same color or font size over and over again throughout your code, you define them once here, and then reference them everywhere else. This is like having a single recipe book instead of writing down the same recipe on every page of your kitchen notebook.

Why This Matters?

  1. Consistency: Everything looks the same throughout your app
  2. Easy to Change: Want to change your accent color? Change it once here, and it updates everywhere
  3. Less Code: Write styles once, reference them many times
  4. Clear Communication: Names like T.gold and S.card are self-documenting
  5. Fewer Errors: Less chance of typos or slightly different colors in different places
// src/theme.ts
//
// APP-WIDE DESIGN SYSTEM
// ───────────────────────
// Single source of truth for all visual constants in Prayer Companion.
//
// USAGE:
//   import { T, S } from "../../src/theme";
//
//   // In StyleSheet.create():
//   title: { color: T.gold, fontSize: 22, fontWeight: "700" }
//
//   // Shared style fragments:
//   <Text style={S.screenTitle}>Prayer Companion</Text>
//   <View style={[S.card, { marginTop: 8 }]}>...</View>

import { StyleSheet, Platform } from "react-native";
// Breaking Down the Components
// ══════════════════════════════════════════════════════════════════════════════
// COLOUR TOKENS  (T for Tokens)
// ══════════════════════════════════════════════════════════════════════════════

export const T = {
  // The T stands for "Tokens" - these are our app's color palette. Each color has a meaningful name:
  // ── Backgrounds. Different shades for screens, panels, cards, and modals ─────────────────────
  /** Main screen background — deep navy */
  bg:       "#1a1f2e",
  /** Slightly lighter panel / sidebar background */
  panel:    "#1e2338",
  /** Card and elevated surface background */
  panel2:   "#222840",
  /** Deeper elevated surface (modals, dropdowns) */
  panel3:   "#2a3050",
  /** Scrim behind modals */
  overlay:  "rgba(0, 0, 0, 0.65)",

  // ── Borders & dividers. Colors for lines that separate sections ────────────────────────────────
  border:   "#2d3550",
  divider:  "#1e2338",

  // ── Text. Different text colors - from main text to muted/dim text ─────────────────────────────
  text:     "#dde4f0",
  muted:    "#9ca3af",
  faint:    "#6b7280",
  dim:      "#4b5563",

  // ── Accent colours ──────────────────────────────────────────────────────────
  gold:        "#c9a84c",
  goldBg:      "#2a2010",
  goldBorder:  "#c9a84c",
  accent:      "#5b8fd4",
  accentBg:    "#1a2438",
  accentBorder:"#1e3a5f",

  // ── Semantic colours ─────────────────────────────────────────────────────────
  // Colors that convey meaning: success (green) - for things that worked
  // warning (yellow/orange) - for things that need attention; error (red) - for things that failed
  success:       "#86efac",
  successBg:     "#1a2e1a",
  successBorder: "#2d4a2d",
  warning:       "#f59e0b",
  warningBg:     "#2e2a1a",
  warningBorder: "#4a3d2d",
  error:         "#f87171",
  errorBg:       "#2e1a1a",
  errorBorder:   "#4a2d2d",

  // ── Misc ──────────────────────────────────────────────────────────────────────
  white:    "#ffffff",
  black:    "#000000",
// The as const at the end tells TypeScript these values should never change.
} as const;

export type ColorToken = keyof typeof T;

// ══════════════════════════════════════════════════════════════════════════════
// TYPOGRAPHY (Font Sizes & Families)
// ══════════════════════════════════════════════════════════════════════════════
// This creates a consistent hierarchy of text sizes.
// Instead of randomly picking "18" or "19" for a heading, you always use h1, h2, etc.
export const FontSize = {
  xs: 10,    // Extra small
  sm: 11,    // Small
  md: 12,    // Medium
  base: 13,  // Default
  lg: 14,    // Large
  xl: 15,    // Extra large
  xxl: 16,   // Double extra large
  h3: 18,    // Heading level 3
  h2: 20,    // Heading level 2
  h1: 22,    // Heading level 1
  hero: 26,  // Hero text (big statement)
  display: 32, // Display text (biggest)
} as const;

// The FontFamily handles platform differences:
// iOS uses Apple's system font; Android uses the default sans-serif font
export const FontFamily = {
  sans: Platform.OS === "ios" ? "-apple-system" : "sans-serif",
  mono: Platform.OS === "ios" ? "Courier New"   : "monospace",
} as const;


// ══════════════════════════════════════════════════════════════════════════════
// SPACING & RADIUS
// ══════════════════════════════════════════════════════════════════════════════
// Spacing controls the space between elements (padding, margins).
// By using consistent spacing values, our app looks more professional and organized.
export const Spacing = {
  xs: 4,    // Tiny gap
  sm: 8,    // Small gap
  md: 12,   // Medium gap
  base: 16, // Standard gap
  lg: 20,   // Large gap
  xl: 24,   // Extra large gap
  xxl: 32,  // Double extra large
  huge: 40, // Huge gap
} as const;
// Radius controls how rounded corners are:
export const Radius = {
  sm:   4, // slightly rounded
  md:   6,
  base: 8,
  lg:   10,
  xl:   12,
  pill: 20,
  full: 9999, // completely round (for circles/pills)
} as const;


// ══════════════════════════════════════════════════════════════════════════════
// LAYOUT HELPERS
// ══════════════════════════════════════════════════════════════════════════════

/**
 * Calculate the sidebar width for two-column layouts.
 * 1. Takes the screen width
 * 2. Calculates 35% of that width
 * 3. Makes sure it's never wider than 200 pixels
 * 4. Rounds down to a whole number
 *
 * It should be consistent across all tabs (Verse, Videos, Online, Prayers, Diary).
 *
 * @example
 * const { width } = useWindowDimensions();
 * const sidebar = sidebarWidth(width);
 */
export function sidebarWidth(screenWidth: number): number {
  return Math.min(200, Math.floor(screenWidth * 0.35));
}


// ══════════════════════════════════════════════════════════════════════════════
// SHARED STYLE FRAGMENTS  (S for Shared)
// ══════════════════════════════════════════════════════════════════════════════
// These are pre-made style combinations that you'll use repeatedly.
export const S = StyleSheet.create({

  // ── Roots ───────────────────────────────────────────────────────────────────
  screen:  { flex: 1, backgroundColor: T.bg },
  twoCol:  { flex: 1, flexDirection: "row", backgroundColor: T.bg },
  center:  { flex: 1, backgroundColor: T.bg, alignItems: "center",
             justifyContent: "center", padding: Spacing.xxl },

  // ── Dividers ────────────────────────────────────────────────────────────────
  vDivider: { width: 1, backgroundColor: T.border },
  hDivider: { height: 1, backgroundColor: T.border },

  // ── Cards ────────────────────────────────────────────────────────────────────
  // For example: S.card - a standard card with background color, rounded corners, padding, and border
  card: { backgroundColor: T.panel2, borderRadius: Radius.lg,
          padding: Spacing.base, borderWidth: 1, borderColor: T.border },

  // ── Typography ───────────────────────────────────────────────────────────────
  // S.screenTitle - the style for main screen titles
  screenTitle:  { fontSize: FontSize.h1,   fontWeight: "700", color: T.gold },
  sectionTitle: { fontSize: FontSize.sm,   fontWeight: "700", color: T.faint,
                  textTransform: "uppercase", letterSpacing: 0.8 },
  cardTitle:    { fontSize: FontSize.lg,   fontWeight: "600", color: T.gold },
  body:         { fontSize: FontSize.xl,   color: T.text, lineHeight: 24 },
  bodyMuted:    { fontSize: FontSize.base, color: T.muted, lineHeight: 20 },
  mono:         { fontSize: FontSize.sm,   color: T.faint, fontFamily: FontFamily.mono },
  hint:         { fontSize: FontSize.xs,   color: T.dim,   fontStyle: "italic" },
  error:        { fontSize: FontSize.base, color: T.error },

  // ── Buttons ──────────────────────────────────────────────────────────────────
  // S.btnPrimary - the style for primary buttons
  btnPrimary: { backgroundColor: T.accent, borderRadius: Radius.base,
                paddingVertical: Spacing.md, paddingHorizontal: Spacing.xl,
                alignItems: "center" },
  btnPrimaryText:   { color: T.white, fontSize: FontSize.lg, fontWeight: "700" },
  btnDisabled:      { backgroundColor: T.border },
  btnDisabledText:  { color: T.faint },
  btnDanger:        { backgroundColor: T.errorBg, borderRadius: Radius.base,
                      paddingVertical: Spacing.sm, paddingHorizontal: Spacing.md,
                      borderWidth: 1, borderColor: T.errorBorder },
  btnDangerText:    { fontSize: FontSize.md, color: T.error },

  // ── Sidebar ──────────────────────────────────────────────────────────────────
  sidebar:      { backgroundColor: T.panel },
  sidebarHeader: { flexDirection: "row", justifyContent: "space-between",
                   alignItems: "center", paddingHorizontal: Spacing.md,
                   paddingVertical: Spacing.md - 2, borderBottomWidth: 1,
                   borderBottomColor: T.border },
  sidebarFooter: { paddingVertical: Spacing.md, paddingHorizontal: Spacing.md,
                   borderTopWidth: 1, borderTopColor: T.border, alignItems: "center" },
  sidebarCount:  { fontSize: FontSize.xs, color: T.accent,
                   backgroundColor: T.accentBg, paddingHorizontal: Spacing.xs + 1,
                   paddingVertical: 1, borderRadius: Radius.pill, overflow: "hidden" },
  separator:     { height: 1, backgroundColor: T.divider },

  // ── Badges ───────────────────────────────────────────────────────────────────
  todayBadge:     { backgroundColor: T.goldBg, borderRadius: Radius.sm,
                    paddingHorizontal: Spacing.xs + 2, paddingVertical: 2,
                    borderWidth: 1, borderColor: T.goldBorder },
  todayBadgeText: { fontSize: FontSize.xs, color: T.gold, fontWeight: "600" },
  todayDot:       { width: 6, height: 6, borderRadius: Radius.full,
                    backgroundColor: T.gold, flexShrink: 0 },
  onlineBadge:    { backgroundColor: T.successBg, borderRadius: Radius.pill,
                    paddingVertical: 3, paddingHorizontal: Spacing.sm + 2,
                    borderWidth: 1, borderColor: T.successBorder },
  offlineBadge:   { backgroundColor: T.accentBg, borderRadius: Radius.pill,
                    paddingVertical: 3, paddingHorizontal: Spacing.sm + 2,
                    borderWidth: 1, borderColor: T.accentBorder },
  badgeText:      { fontSize: FontSize.sm, fontWeight: "600", color: T.text },

  // ── Selected item left border ─────────────────────────────────────────────
  selectedBorder:       { position: "absolute", left: 0, top: 0, bottom: 0,
                          width: 2, backgroundColor: T.gold },
  selectedBorderAccent: { position: "absolute", left: 0, top: 0, bottom: 0,
                          width: 2, backgroundColor: T.accent },

  // ── Status cards ─────────────────────────────────────────────────────────────
  successCard: { backgroundColor: T.successBg, borderRadius: Radius.lg,
                 padding: Spacing.base, borderWidth: 1, borderColor: T.successBorder,
                 alignItems: "center" },
  errorCard:   { backgroundColor: T.errorBg, borderRadius: Radius.lg,
                 padding: Spacing.base, borderWidth: 1, borderColor: T.errorBorder,
                 alignItems: "center" },
});

Step 2 — Update app/index.tsx (home screen)

What is the Home Screen?

This is your app’s landing page or welcome screen. It’s the entry point that users see before they navigate into the main app. Think of it like the front door of your house - it’s the first thing people encounter and it should make a good impression.

The home screen (app/index.tsx) is special because:

// React - The core React library needed for JSX components.
import React from "react";
// View - Like a DIV in web development, just a container
// Text - For displaying any text on screen
// StyleSheet - React Native's way of organizing styles (like CSS but in JavaScript)
// TouchableOpacity - Makes elements tappable/clickable, with a fade effect when pressed
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
// router from expo-router - This is how you navigate between screens. It's like having a GPS for your app.
import { router } from "expo-router";
// StatusBar from expo-status-bar - Controls the phone's status bar (time, battery, etc.)
import { StatusBar } from "expo-status-bar";
// Theme imports from ../src/theme:
// T - Your theme colors (like T.bg, T.muted); S - Your shared/reusable styles (like S.screenTitle, S.btnPrimary)
// FontSize - Your font size definitions, Spacing - Your spacing definitions
import { T, S, FontSize, Spacing } from "../src/theme";

export default function HomeScreen() {
  return (
    // The Main Container. This is the main wrapper that holds everything.
    // flex: 1 - Takes up the entire screen; backgroundColor: T.bg - Dark background from your theme
    // alignItems: "center" - Centers children horizontally; justifyContent: "center" - Centers children vertically
    // padding: Spacing.xxl - Extra-large padding around the edges
    // This creates a centered layout where everything is in the middle of the screen.
    <View style={styles.container}>
      // This makes the status bar text/icons white (light style) so they're visible against your dark background.
      <StatusBar style="light" />
      // The Content Elements:
      // 1. Icon. This displays a prayer emoji (folded hands).
      // It's using Text component because emojis are just text characters.
      // The style makes it large (64 pixels) with some space below.
      <Text style={styles.icon}>🙏</Text>
      // 2. Title. This shows our app name. Notice it uses an array of styles:
      // (*) S.screenTitle - The shared screen title style from the theme
      // (*) styles.title - Additional styles for this specific usage (centering and spacing)
      // When you pass an array of styles, they merge together, with later styles overriding earlier ones if there's a conflict.
      <Text style={[S.screenTitle, styles.title]}>Prayer Companion</Text>
      // 3. Subtitle. A welcoming message.
      // The style makes it: Base font size (fontSize: FontSize.base); Muted color (lighter, less prominent); Centered text
      // Italic; Extra spacing below
      <Text style={styles.subtitle}>Begin each day in God's presence      // 4. Description. Lists the main features.
      // Uses medium font size, muted color, centered, with lots of space below and increased line height for readability.
      
        Daily scripture · Prayer library · Diary · Meditation
            // 5. Button. This is your main call-to-action button.
              style={S.btnPrimary}  // Uses your shared primary button style from the theme
        onPress={() => router.push("/(tabs)/verse")} // When pressed, navigates to the verse screen in the tabs
        // push means "add this screen to the navigation stack"
        // /(tabs)/verse is the route to the verse screen inside the tabs
        // The parentheses in (tabs) indicate a group, not part of the actual URL
        activeOpacity={0.8} // When pressed, the button becomes 80% opaque (slightly transparent) for visual feedback
      >
        Open Prayer Companion            // 6. Version Number.
      // Shows the app version. The style positions it:
      // Absolutely positioned at the bottom of the screen (position: "absolute", bottom: Spacing.xl);
      // Uses the shared hint style (small, muted text); Extra spacing from the bottom
      v1.0.0      );
}

// The StyleSheet.create is React Native's way of organizing styles.
// It's like having a CSS file but in JavaScript.
// Each style is defined as an object with camelCase property names (like backgroundColor instead of background-color).
const styles = StyleSheet.create({
  container:   { flex: 1, backgroundColor: T.bg, alignItems: "center",
                 justifyContent: "center", padding: Spacing.xxl },
  icon:        { fontSize: 64, marginBottom: Spacing.base },
  title:       { textAlign: "center", marginBottom: Spacing.sm },
  subtitle:    { fontSize: FontSize.base, color: T.muted, textAlign: "center",
                 marginBottom: Spacing.xl, fontStyle: "italic" },
  description: { fontSize: FontSize.md, color: T.muted, textAlign: "center",
                 marginBottom: Spacing.huge, lineHeight: 22 },
  version:     { position: "absolute", bottom: Spacing.xl },
});

Step 3 — Update app/_layout.tsx (ROOT layout — keep SQLiteProvider!)

What is a Root Layout?

Think of the root layout as the outer shell or container of your entire application. Every single screen in your app lives inside this container. It’s like the frame of a house - it defines the overall structure that everything else fits into.

In our app, we have two types of layout files:

  1. Root Layout (app/_layout.tsx) - This one! It wraps the ENTIRE app
  2. Tab Layout (app/(tabs)/_layout.tsx) - This only wraps the tab screens
// app/_layout.tsx
//
// ROOT LAYOUT — wraps the entire app. The only addition is the theme import.
//
// The root layout is at app/_layout.tsx. DO NOT confuse with app/(tabs)/_layout.tsx:
// To get to src/theme, you go up one level from app/ to the project root
//  Then into src/ and to the theme file. So it's ../src/theme (one level up)
//
// Stack from expo-router - This is the navigation system.
// A "stack" is like a pile of cards where you can push new screens on top and pop them off to go back.
// It's the most common navigation pattern.
import { Stack } from "expo-router";
// StatusBar from expo-status-bar - Controls the status bar at the top of the phone screen
// (where the time, battery, and signal strength appear).
import { StatusBar } from "expo-status-bar";
// Suspense from react - This is React's way of handling "loading states."
// It shows a fallback (like a spinner) while something is loading.
import { Suspense } from "react";
// ActivityIndicator is the spinning loading animation, and View is like a DIV in web development
// - just a container for other elements.
import { ActivityIndicator, View } from "react-native";
// This sets up your database connection for the whole app.
import { SQLiteProvider } from "expo-sqlite";
// These are the database name and the function that sets up your database tables.
import { DB_NAME, initDatabase } from "../src/db/database";
// T from your theme file - This is your theme object that contains all your color definitions \
// like T.bg for background, T.gold for gold accents, or T.text for text color.
import { T } from "../src/theme";

// Understanding the Component Structure
// Let us break down what's happening inside the RootLayout component:
export default function RootLayout() {
  return (
    // 1. Suspense Wrapper
    // This is saying: "While the database is loading, show a loading spinner."
    // The Suspense component watches for anything inside it that needs to load (like your database)
    // and shows the fallback UI during that time.
    <Suspense
      // The fallback is:
      // (i) A full-screen view (flex: 1) with your app's background color (backgroundColor: T.bg)
      // (ii) Centered content (both horizontally and vertically)
      // (iii) A large gold spinner
      fallback={
        <View style={{ flex: 1, backgroundColor: T.bg,
                        alignItems: "center", justifyContent: "center" }}>
          <ActivityIndicator size="large" color={T.gold} />
        </View>
      }
    >
      // 2. SQLiteProvider
      // This connects your app to the database.
      // The useSuspense (🔧) prop tells it to work with the Suspense component above
      // The onInit (🔗) runs your initDatabase function when the database first opens.
      <SQLiteProvider
        databaseName={DB_NAME}
        onInit={initDatabase} // (🔗)
        useSuspense // (🔧)
      >
        // 3. StatusBar
        // This makes the status bar text/icons light-colored (white), which makes sense since your background is dark.
        <StatusBar style="light" />
        // 4. Stack Navigator
        // This is where the theming really happens.
        <Stack
          // The screenOptions apply to ALL screens in your app.
          // By putting these styles here in the root layout, you ensure EVERY screen in your app has consistent styling.
          // You don't have to repeat these settings on each individual screen - they're inherited automatically.
          screenOptions={{
            // The background color of the header bar at the top of each screen
            headerStyle:      { backgroundColor: T.bg },
            // The color of the back button and other interactive elements in the header
            headerTintColor:  T.text,
            // How the title text looks (bold, with your text color)
            headerTitleStyle: { fontWeight: "bold", color: T.text },
            // The background color of the screen content area
            contentStyle:     { backgroundColor: T.bg },
          }}
        />
      </SQLiteProvider>
    </Suspense>
  );
}

Step 4 — Update app/(tabs)/_layout.tsx (TAB layout — tab bar only)

What is the Tab Layout? The tab layout is responsible for the bottom tab bar - those icons at the bottom of the screen that let users switch between different sections of your app. Think of it like the navigation menu at the bottom of apps like Instagram, Twitter, or YouTube.

This file is at app/(tabs)/_layout.tsx, which means:

// app/(tabs)/_layout.tsx
//
// TAB LAYOUT — configures the bottom tab bar only.

// This is the tab navigation system. It creates the bottom bar with all your tab buttons.
import { Tabs } from "expo-router";
// Platform: This tells you what device the app is running on (iOS or Android).
// This matters because iOS and Android have different design guidelines and safe areas.
// Text from react-native - Used for displaying text (in this case, emojis as icons).
import { Platform, Text } from "react-native";
// T from your theme - Your theme colors.
// Notice the import path is "../../src/theme" with TWO dots segments because this file is two levels deep.
// Up one level: from (tabs)/ to app/ (first ..). Up another level: from app/ to the project root (second ..)
// Then, into src/ and to theme
import { T } from "../../src/theme";

// The Main Tabs Container
export default function TabLayout() {
  return (
    <Tabs
      // The screenOptions apply to ALL tab screens.
      screenOptions={{
        // This styles the bottom tab bar itself:
        tabBarStyle: {
          backgroundColor: T.panel, // The tab bar background uses your "panel" color
          borderTopColor:  T.border, // A subtle line at the top of the tab bar
          borderTopWidth:  1, // That line is 1 pixel thick
          paddingBottom:   Platform.OS === "ios" ? 20 : 8,
          // Extra space at the bottom. iOS gets 20 pixels (because iPhones have a "home indicator" area at the bottom
          // that apps need to avoid), Android gets just 8 pixels
          paddingTop:      8, //  Space at the top of the tab bar
          height:          Platform.OS === "ios" ? 80 : 60,
          // The total height. iOS gets 80 pixels (taller due to home indicator), Android gets 60 pixels
        },
        // Tab Colors. This provides visual feedback so users always know which tab they're on.
        // Active tab (the one currently selected) gets your gold color
        tabBarActiveTintColor:   T.gold,
        // Inactive tabs get a faint/muted color
        tabBarInactiveTintColor: T.faint,
        // The text labels under each icon: Small font size (11 pixels);
        // Semi-bold weight (600); Slight space above the label.
        tabBarLabelStyle: { fontSize: 11, fontWeight: "600", marginTop: 2 },
        // Header Styling
        // Dark background, with shadows removed (shadowOpacity: 0 for iOS, elevation: 0 for Android)
        headerStyle:      { backgroundColor: T.bg, shadowOpacity: 0, elevation: 0 },
        // The title text is bold, 18 pixels, in your text color
        headerTitleStyle: { color: T.text, fontWeight: "700", fontSize: 18 },
        // Interactive elements (like back buttons) are in your text color
        headerTintColor:  T.text,
      }}
    >

Each Tabs.Screen has:

  1. name - This must match a file name in the (tabs) folder. So name=“verse” corresponds to app/(tabs)/verse.tsx.
  2. title - The text shown in the tab bar
  3. headerTitle - The text shown in the header when this tab is active
  4. tabBarIcon - A function that returns the icon for this tab

Let me list all five tabs:

  1. verse - “📖 Scripture” - For daily Bible verses
  2. videos - “🙏 Prayer Videos” - For video content
  3. online - “🌐 Prayer Sites” - For external prayer resources
  4. prayers - “✝ My Prayers” - For personal prayer library
  5. diary - “📔 Prayer Diary” - For journaling

Why Use Emojis as Icons?

Using emojis as icons is a simple approach that avoids needing to install and configure icon libraries. While professional apps often use custom icons, emojis:

      <Tabs.Screen name="verse"   options={{ title: "Verse",
        headerTitle: "📖  Scripture",
        tabBarIcon: ({ color }) => <TabIcon emoji="📖" color={color} /> }} />
      <Tabs.Screen name="videos"  options={{ title: "Videos",
        headerTitle: "🙏  Prayer Videos",
        tabBarIcon: ({ color }) => <TabIcon emoji="🙏" color={color} /> }} />
      <Tabs.Screen name="online"  options={{ title: "Online",
        headerTitle: "🌐  Prayer Sites",
        tabBarIcon: ({ color }) => <TabIcon emoji="🌐" color={color} /> }} />
      <Tabs.Screen name="prayers" options={{ title: "Prayers",
        headerTitle: "✝  My Prayers",
        tabBarIcon: ({ color }) => <TabIcon emoji="✝" color={color} /> }} />
      <Tabs.Screen name="diary"   options={{ title: "Diary",
        headerTitle: "📔  Prayer Diary",
        tabBarIcon: ({ color }) => <TabIcon emoji="📔" color={color} /> }} />
    </Tabs>
  );
}

// TabIcon Component. This is a helper component that creates tab icons using emojis.
// Parameters: emoji - Which emoji to display;
// In React Navigation’s tab bar, every icon receives a color prop automatically.
// tabBarIcon: ({ color }) => <TabIcon emoji="📖" color={color} /> }} />
// If the tab is active, it passes: color = tabBarActiveTintColor.
// If the tab is inactive, it passes: color = tabBarInactiveTintColor
// Since we have already configured: tabBarActiveTintColor: T.gold, tabBarInactiveTintColor: T.faint,
// When the tab is active: icon({ color: T.gold, size: 24 })
function TabIcon({ emoji, color }: { emoji: string; color: string }) {
  // It determines if this tab is active by checking if the color matches your gold theme color (T.gold).
  const isActive = color === T.gold;
  return (
    // Font size 22 pixels. Opacity - Active tabs are fully opaque (1.0), inactive tabs are 45% transparent (0.45)
    // Centered text. Slight negative margin to align better
    <Text style={{ fontSize: 22, opacity: isActive ? 1.0 : 0.45,
                   textAlign: "center", marginBottom: -2 }}>
      {emoji}
    </Text>
  );
}

The Navigation Hierarchy

Root Layout (app/_layout.tsx)
├── SQLiteProvider (database)
├── Suspense (loading state)
└── Stack Navigator
    ├── Home Screen (app/index.tsx)
    └── Tab Layout (app/(tabs)/_layout.tsx)
        ├── Verse Tab (app/(tabs)/verse.tsx)
        ├── Videos Tab (app/(tabs)/videos.tsx)
        ├── Online Tab (app/(tabs)/online.tsx)
        ├── Prayers Tab (app/(tabs)/prayers.tsx)
        └── Diary Tab (app/(tabs)/diary.tsx)

The home screen acts as a “launch” screen, and when users tap “Open Prayer Companion,” they enter the tab navigation where they can switch between the five main sections.

Steps 5–9 — Replace the five tab files

We’re going to refactor five tab files to stop using local color/style definitions and instead use a centralized theme system. This is a common pattern in React Native/React projects where you want consistent styling across your app.

Each tab file currently has its own local definition of colors and styles like this:

const C = {
  bg: "#1a1f2e",
  panel: "#1e2338",
  // ... more colors
};

This means:

  1. Add the import. At the top of each tab file, add:

    import { T, S, FontSize, Spacing, Radius } from "../../src/theme";
    
  2. Delete the local const C block entirely.
    Find and completely remove the const C = { … } block from each file. This eliminates the duplication and forces the file to use the centralized theme.

  3. Replace All Color References, every C.xxx reference with T.xxx:

    # In your editor, use Find & Replace in the current file:
    C.bg      → T.bg (background color)
    C.panel   → T.panel (panel background)
    C.panel2  → T.panel2  (secondary panel)
    C.border → T.border (border colors)
    C.accent → T.accent (accent/highlight color)
    C.text → T.text (primary text)
    C.muted → T.muted (muted/secondary text)
    C.faint → T.faint (very light/faint text)
    C.gold → T.gold (gold/yellow accent)
    C.dim → T.dim (dimmed elements)
    C.white → T.white (white color)
    C.black → T.black (black color)
    C.red → T.error (red is now called "error" - more semantic)
    C.green → T.success (green is now called "success" - more semantic)
    
  4. Replace common StyleSheet patterns with S.* fragments where applicable.
    For example, instead of writing out a full shadow style every time, you might use S.shadow if it exists in the theme.
    This is optional — mixing local styles with S.* is perfectly fine.

Specific Example: verse.tsx

In app/(tabs)/verse.tsx, the top of the file has:

const C = {
  bg:     "#1a1f2e",
  panel:  "#1e2338",
  // ... more colors
};

Replace it with:

import { T, S, FontSize, Spacing } from "../../src/theme";
// Delete the entire const C = { ... } block
// All C.xxx references replaced with T.xxx

Why This Matters

  1. Consistency: All tabs will now use exactly the same colors
  2. Maintainability: Change a color once in the theme, it updates everywhere
  3. Semantic naming: “error” and “success” are clearer than “red” and “green”
  4. Scalability: Easy to add dark mode or theme switching later
  5. Single source of truth: One file controls all styling values

Step 5 — The test: change one colour, see it everywhere

// In src/theme.ts, change:
gold: "#c9a84c",    // current amber gold

// To:
gold: "#e5a832",    // slightly warmer gold

Save theme.ts. Hot reload updates every screen that imports T — the verse reference text, the prayer title, the diary heading, the tab bar active colour, and today’s badge all change simultaneously.

Change it back:

gold: "#c9a84c",    // restore original
Bitcoin donation

JustToThePoint Copyright © 2011 - 2026 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.