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
| Weight | File | Tailwind Class |
|---|---|---|
| Bold | PlayfairDisplay-Bold.ttf | font-playfair-bold |
Used for headings and product names — a serif typeface that conveys elegance and premium quality.
Montserrat
| Weight | File | Tailwind Class |
|---|---|---|
| Regular | Montserrat-Regular.ttf | font-montserrat |
| Medium | Montserrat-Medium.ttf | font-montserrat-medium |
| Semi-Bold | Montserrat-SemiBold.ttf | font-montserrat-semibold |
| Bold | Montserrat-Bold.ttf | font-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
- "Scan Item" — navigates to
- 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
productDetailsJSON)
- 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
Dropdown Menus (zeego)
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/:
| Image | Usage |
|---|---|
icon.png | App icon |
splash-icon.png | Splash screen logo |
verified.png | Verification badge on details screen |
eye.png | View sealed image icon |
home-certi-lock.png | Home screen Certi-Lock branding |
home-history.png | Home screen history quick action |
header-barcode-scanner.png | Header scan icon |
header-back.png | Header back navigation icon |
header-ellipsis.png | Header dropdown menu trigger |
header-icon.png | Header Certi-Lock logo |
header-security-lock.png | Header security indicator |
fallback-bar-gold.png | Fallback: gold bar product |
fallback-bar-silver.png | Fallback: silver bar product |
fallback-round-gold.png | Fallback: gold round product |
fallback-round-silver.png | Fallback: 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 elementsaccessibilityRole— Semantic roles (button, link, image, header)- Safe Area Insets —
react-native-safe-area-contexthandles 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