Skip to main content

UI & Styling

The Certi-Lock® app uses NativeWind (Tailwind CSS for React Native) for utility-first styling, custom font families for brand consistency, and several UI libraries for interactive components.


NativeWind / Tailwind CSS

Configuration

NativeWind bridges Tailwind CSS to React Native's style system. It is configured at three levels:

1. Metro Bundler:

// metro.config.js
const { withNativeWind } = require("nativewind/metro");
module.exports = withNativeWind(config, { input: "./global.css" });

2. Babel Transpilation:

// babel.config.js
module.exports = {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
};

3. Tailwind Theme:

// tailwind.config.js
module.exports = {
content: ["./app/**/*.{js,jsx,ts,tsx}"],
presets: [require("nativewind/preset")],
theme: {
extend: {
fontFamily: {
"playfair-bold": ["PlayfairDisplay-Bold"],
montserrat: ["Montserrat"],
"montserrat-medium": ["Montserrat-Medium"],
"montserrat-semibold": ["Montserrat-SemiBold"],
"montserrat-bold": ["Montserrat-Bold"],
},
},
},
};

Usage Pattern

NativeWind enables Tailwind utility classes directly in React Native components via the className prop:

<View className="flex-1 bg-white px-4 py-6">
<Text className="font-playfair-bold text-2xl text-gray-900">
Certi-Lock® Verified
</Text>
<Text className="font-montserrat text-base text-gray-600 mt-2">
Your item has been verified.
</Text>
</View>

Custom Fonts

The app uses two font families for brand consistency with Scottsdale Mint's visual identity:

Playfair Display

WeightFileTailwind Class
BoldPlayfairDisplay-Bold.ttffont-playfair-bold

Used for headings and product names — a serif typeface that conveys elegance and premium quality.

Montserrat

WeightFileTailwind Class
RegularMontserrat-Regular.ttffont-montserrat
MediumMontserrat-Medium.ttffont-montserrat-medium
Semi-BoldMontserrat-SemiBold.ttffont-montserrat-semibold
BoldMontserrat-Bold.ttffont-montserrat-bold

Used for body text, labels, and UI elements — a clean sans-serif for readability.

Font Loading

Fonts are loaded via Expo's font system at app startup. The app displays a splash screen until all fonts are loaded.


Screen Layouts

Home Screen (/)

The home/dashboard screen features:

  • Promotional Banner Carousel — Full-width swipeable banners from WordPress
  • Quick Action Buttons — Two primary actions:
    • "Scan Item" — navigates to /scan
    • "View History" — navigates to /history
  • Branding Elements — Certi-Lock logo and tagline

Scan Screen (/scan)

  • Full-Screen Camera Preview — Camera fills the entire screen
  • Scanning Overlay — Visual indicator showing the scan area
  • Loading State — Activity indicator during save operation
  • Permission Prompt — Shown when camera access is not granted

History Screen (/history)

  • Search Bar — Filter items by name or serial number
  • Item List — Vertical scrollable list ordered by most recent
    • Each row displays product name, serial number, and thumbnail
    • Swipe-to-Delete — Swipe left to reveal delete action
    • Confirmation Dialog — Confirms before permanent deletion
  • Empty State — Messaging when no items have been scanned

Details Screen (/details/[id])

  • Verification Badge — "Verified" indicator with icon
  • Product Image — Full-width image (local cache or fallback)
  • Product Information — Two-column layout:
    • Product name and subtitle
    • Serial number
    • Sealing date
    • Product specifications (from productDetails JSON)
  • Dropdown Menu — Accessed via header ellipsis icon:
    • Delete item
    • View sealed image
    • Start new scan

Sealed Image Screen (/details/[id]/sealed)

  • Full-Resolution Image — Loaded from local filesystem using SHA256 hash
  • Fallback Message — "Image still processing" if no local image exists
  • Back Navigation — Returns to item details

Component Library

Context-specific dropdown menus in the header use the zeego library, which provides native-feeling menus on both iOS (UIMenu) and Android (Material Menu):

import * as DropdownMenu from "zeego/dropdown-menu";

<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Image source={headerEllipsis} />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item key="delete" onSelect={handleDelete}>
<DropdownMenu.ItemTitle>Delete Item</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
<DropdownMenu.Item key="sealed" onSelect={handleViewSealed}>
<DropdownMenu.ItemTitle>View Sealed Image</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
<DropdownMenu.Item key="scan" onSelect={handleNewScan}>
<DropdownMenu.ItemTitle>New Scan</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>

Toast Notifications

react-native-toast-message provides non-blocking feedback for user actions:

import Toast from "react-native-toast-message";

// Success
Toast.show({
type: "success",
text1: "Item Saved",
text2: "Product has been added to your history.",
});

// Error
Toast.show({
type: "error",
text1: "Scan Failed",
text2: "Unable to retrieve product data. Please try again.",
});

// Info (duplicate detection)
Toast.show({
type: "info",
text1: "Already Scanned",
text2: "This serial number is already in your history.",
});

The <Toast /> component is rendered in the root layout, above all screens.

Gestures

react-native-gesture-handler enables swipe-to-delete in the history list:

  • Swipe Left — Reveals red "Delete" action button
  • Tap Delete — Shows confirmation dialog
  • Confirm — Deletes item from SQLite and removes cached image

Animations

react-native-reanimated provides smooth animations for:

  • Screen transitions (Expo Router stack navigation)
  • Swipe-to-delete gestures
  • Loading state transitions

Asset Management

Static Images

All static images are stored in assets/images/:

ImageUsage
icon.pngApp icon
splash-icon.pngSplash screen logo
verified.pngVerification badge on details screen
eye.pngView sealed image icon
home-certi-lock.pngHome screen Certi-Lock branding
home-history.pngHome screen history quick action
header-barcode-scanner.pngHeader scan icon
header-back.pngHeader back navigation icon
header-ellipsis.pngHeader dropdown menu trigger
header-icon.pngHeader Certi-Lock logo
header-security-lock.pngHeader security indicator
fallback-bar-gold.pngFallback: gold bar product
fallback-bar-silver.pngFallback: silver bar product
fallback-round-gold.pngFallback: gold round product
fallback-round-silver.pngFallback: silver round product

Dynamic Images

Sealed product images are stored in the app's document directory with SHA256 hash filenames. These are loaded using { uri: filePath } image sources.


Accessibility

The app follows React Native accessibility patterns:

  • accessibilityLabel — Descriptive labels for interactive elements
  • accessibilityRole — Semantic roles (button, link, image, header)
  • Safe Area Insetsreact-native-safe-area-context handles notches, status bars, and home indicators
  • Touch Targets — Minimum 44x44 point touch targets for interactive elements
  • Color Contrast — Text colors meet WCAG AA contrast ratios against backgrounds