Airline boarding pass with barcode
import * as React from "react";
import { ScrollView, View } from "react-native";
import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context";
import { Button, Glass, Icon, Text } from "@lumioui/ui";
import { MONO, Mount, World } from "../shared";
import { useHaptic } from "@lumioui/core";
export interface BoardingPassScreenProps {
passenger?: string;
flight?: string;
from?: string;
to?: string;
depart?: string;
arrive?: string;
gate?: string;
seat?: string;
group?: string;
/** Barcode stripes — the scanner-readable part of the pass. */
barcode?: number[];
onBack?: () => void;
}
const DEFAULT_BARCODE = [
3, 1, 4, 2, 5, 1, 2, 4, 1, 3, 5, 2, 4, 1, 3, 2, 5, 4, 1, 3, 2, 4, 5, 1, 3, 2, 4, 1, 5, 3, 2, 4,
];
/** Boarding pass — airline card with route arc + scannable barcode. */
export function BoardingPassScreen({
passenger = "Amara Chen",
flight = "TP 268",
from = "SFO",
to = "LIS",
depart = "8:15 PM",
arrive = "2:40 PM +1",
gate = "B14",
seat = "14A",
group = "3",
barcode = DEFAULT_BARCODE,
onBack,
}: BoardingPassScreenProps) {
const haptics = useHaptic();
const insets = useSafeAreaInsets();
return (
<World primary="#65A30D">
<SafeAreaView className="flex-1 bg-[#F4F5F9] dark:bg-[#111114]" edges={["top", "bottom"]}>
<View className="flex-row items-center gap-2 px-4 pb-2 pt-2">
<Button
variant="ghost"
size="icon"
haptic="light"
onPress={() => onBack?.()}
accessibilityLabel="Go back"
className="-ml-2 h-10 w-10"
>
<Icon name="chevron-left" size="md" color="foreground" />
</Button>
</View>
<Mount>
<Text variant="h2" accessibilityRole="header" className="px-6 pb-1 tracking-tight">
Boarding pass
</Text>
{/* Gate countdown — passes live on the clock. */}
<Text variant="muted" className="px-6 pb-1 text-xs">
Boarding in 38 min · gate closes 9:50 AM
</Text>
</Mount>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: insets.bottom + 24 }}
>
{/* The pass — one tonal card; the tinted canvas supplies the edge. */}
<Mount className="mx-4 overflow-hidden rounded-3xl bg-white dark:bg-[#1A1A20]">
{/* Airline strip — dark header band. */}
<View className="flex-row items-center justify-between bg-foreground px-5 py-3">
<Text className="text-sm font-semibold text-background">
{flight}
</Text>
<Text className="text-xs text-background/70">
Economy · Group {group}
</Text>
</View>
{/* Route — big airport codes + flight line. */}
<View className="flex-row items-center justify-between px-5 py-4">
<View>
<Text className="text-3xl font-bold tracking-tight">{from}</Text>
<Text className="text-xs text-muted-foreground">
San Francisco
</Text>
<Text className="pt-1 text-sm font-medium">{depart}</Text>
</View>
<View className="flex-1 items-center px-3">
<Icon name="plane" size="md" color="mutedForeground" />
<View className="mt-1 h-px w-full bg-border" />
</View>
<View className="items-end">
<Text className="text-3xl font-bold tracking-tight">{to}</Text>
<Text className="text-xs text-muted-foreground">Lisbon</Text>
<Text className="pt-1 text-sm font-medium">{arrive}</Text>
</View>
</View>
{/* Perforation — the torn-edge convention on real passes. */}
<View className="mx-4 border-t border-dashed border-border" />
{/* Passenger + seat block. */}
<View className="flex-row justify-between px-5 py-4">
<View>
<Text className="text-[10px] uppercase text-muted-foreground">
Passenger
</Text>
<Text className="text-sm font-semibold">{passenger}</Text>
</View>
<View className="items-end">
<Text className="text-[10px] uppercase text-muted-foreground">
Seat
</Text>
<Text className="text-xl font-bold">{seat}</Text>
</View>
<View className="items-end">
<Text className="text-[10px] uppercase text-muted-foreground">
Gate
</Text>
<Text className="text-xl font-bold">{gate}</Text>
</View>
</View>
{/* Barcode — variable-width stripes like a real 1D code. */}
<View className="items-center px-5 pb-5">
<View className="h-16 w-full flex-row items-stretch justify-center gap-[1px]">
{barcode.map((w, i) => (
<View
key={i}
className="bg-foreground"
style={{ width: w }}
accessibilityElementsHidden
/>
))}
</View>
<Text className="pt-2 text-[10px] tracking-widest text-muted-foreground" style={{ fontFamily: MONO, fontVariant: ["tabular-nums"] }}>
{flight.replace(" ", "")} · {seat} · SFO→LIS
</Text>
</View>
</Mount>
{/* Add-to-wallet affordance — where real passes go. */}
<Mount delay={60} className="mx-4 mt-3">
<Button
variant="secondary"
haptic="light"
onPress={() => haptics.light()}
accessibilityLabel="Add to Wallet"
className="w-full"
>
<Icon name="download" size="sm" color="foreground" />
<Text className="ml-1.5">Add to Wallet</Text>
</Button>
</Mount>
{/* Flight details — the logistics block that rides with the pass. */}
<Mount delay={120} className="mx-4 mt-4 gap-2.5 overflow-hidden rounded-3xl bg-white p-5 dark:bg-[#1A1A20]">
{[
{ label: "Boarding", value: "7:45 PM" },
{ label: "Terminal", value: "2 · Door B" },
{ label: "Duration", value: "11h 25m" },
].map((r) => (
<View key={r.label} className="flex-row justify-between">
<Text className="text-sm text-muted-foreground">{r.label}</Text>
<Text className="text-sm font-semibold">{r.value}</Text>
</View>
))}
</Mount>
</ScrollView>
</SafeAreaView>
</World>
);
}
BoardingPassScreen.displayName = "BoardingPassScreen";
Recreate the "Boarding Pass Screen" screen in React Native using LumioUI.
Goal: Airline boarding pass with barcode Match the reference implementation's layout, behavior, and motion exactly — this is a production screen, not a mockup.
## Setup
Fastest path — install the screen plus its component deps:
```bash
npx lumioui add travel-boarding-pass
```
Or pull just the components: `npx lumioui add button icon text`
Native packages:
- @lumioui/ui@latest
- @lumioui/core@latest
- react-native-safe-area-context@*
- react-native-reanimated@*
- react-native-svg@*
- nativewind@*
Stack assumptions: Expo SDK 54 / RN 0.81, Reanimated 4, NativeWind v4, TypeScript strict, Expo Go compatible.
## Components — how, where, when
### `button` — Tactile pill button with press spring, ink wash and async loading.
- `<Button>` ×2
### `icon` — Lucide icon set mapped to theme colors and sizes.
- `<Icon>` ×3
### `text` — Themed type scale from caption to display.
- `<Text>` ×20
## Conventions (non-negotiable)
- Styling via NativeWind `className` with semantic tokens only: `bg-background`, `text-foreground`, `bg-card`, `text-card-foreground`, `bg-secondary`, `bg-muted`, `text-muted-foreground`, `bg-accent`, `bg-destructive`, `border-border`. Hard-coded colors only inside media overlays (`text-white`, `bg-black/N`).
- Motion via the token presets from `@lumioui/core` (`spring.press`, `spring.surface`, `spring.glide`, `timing.fast`, `timing.slow`, …) — no bespoke spring configs or durations.
- Gate transform motion behind `useReducedMotion()`; keep opacity/color feedback, drop travel and overshoot.
- Haptics via `useHaptic()`: `light` on press, `selection` on toggle/select, `warning` on invalid input. Never import expo-haptics directly.
- Press feedback via `usePressAnimation()` (~0.97 scale), not `activeOpacity` tricks.
- a11y: `accessibilityRole` + `accessibilityLabel` on every control, touch targets ≥44pt.
- Entrance choreography: staggered `Mount` wrappers (delay 0/60/120…) so regions land in sequence instead of popping in at once.
- Wrap the screen in `SafeAreaView`; keep dark mode working via the semantic tokens.
## Deliverable
One file: `screens/travel/boarding-pass.tsx` exporting BoardingPassScreen. Preserve the props interface — the host app wires the callbacks (navigation, submit handlers) through them.
## Reference implementation
The LumioUI source for this screen — reproduce it faithfully:
```tsx
import * as React from "react";
import { ScrollView, View } from "react-native";
import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context";
import { Button, Glass, Icon, Text } from "@lumioui/ui";
import { MONO, Mount, World } from "../shared";
import { useHaptic } from "@lumioui/core";
export interface BoardingPassScreenProps {
passenger?: string;
flight?: string;
from?: string;
to?: string;
depart?: string;
arrive?: string;
gate?: string;
seat?: string;
group?: string;
/** Barcode stripes — the scanner-readable part of the pass. */
barcode?: number[];
onBack?: () => void;
}
const DEFAULT_BARCODE = [
3, 1, 4, 2, 5, 1, 2, 4, 1, 3, 5, 2, 4, 1, 3, 2, 5, 4, 1, 3, 2, 4, 5, 1, 3, 2, 4, 1, 5, 3, 2, 4,
];
/** Boarding pass — airline card with route arc + scannable barcode. */
export function BoardingPassScreen({
passenger = "Amara Chen",
flight = "TP 268",
from = "SFO",
to = "LIS",
depart = "8:15 PM",
arrive = "2:40 PM +1",
gate = "B14",
seat = "14A",
group = "3",
barcode = DEFAULT_BARCODE,
onBack,
}: BoardingPassScreenProps) {
const haptics = useHaptic();
const insets = useSafeAreaInsets();
return (
<World primary="#65A30D">
<SafeAreaView className="flex-1 bg-[#F4F5F9] dark:bg-[#111114]" edges={["top", "bottom"]}>
<View className="flex-row items-center gap-2 px-4 pb-2 pt-2">
<Button
variant="ghost"
size="icon"
haptic="light"
onPress={() => onBack?.()}
accessibilityLabel="Go back"
className="-ml-2 h-10 w-10"
>
<Icon name="chevron-left" size="md" color="foreground" />
</Button>
</View>
<Mount>
<Text variant="h2" accessibilityRole="header" className="px-6 pb-1 tracking-tight">
Boarding pass
</Text>
{/* Gate countdown — passes live on the clock. */}
<Text variant="muted" className="px-6 pb-1 text-xs">
Boarding in 38 min · gate closes 9:50 AM
</Text>
</Mount>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: insets.bottom + 24 }}
>
{/* The pass — one tonal card; the tinted canvas supplies the edge. */}
<Mount className="mx-4 overflow-hidden rounded-3xl bg-white dark:bg-[#1A1A20]">
{/* Airline strip — dark header band. */}
<View className="flex-row items-center justify-between bg-foreground px-5 py-3">
<Text className="text-sm font-semibold text-background">
{flight}
</Text>
<Text className="text-xs text-background/70">
Economy · Group {group}
</Text>
</View>
{/* Route — big airport codes + flight line. */}
<View className="flex-row items-center justify-between px-5 py-4">
<View>
<Text className="text-3xl font-bold tracking-tight">{from}</Text>
<Text className="text-xs text-muted-foreground">
San Francisco
</Text>
<Text className="pt-1 text-sm font-medium">{depart}</Text>
</View>
<View className="flex-1 items-center px-3">
<Icon name="plane" size="md" color="mutedForeground" />
<View className="mt-1 h-px w-full bg-border" />
</View>
<View className="items-end">
<Text className="text-3xl font-bold tracking-tight">{to}</Text>
<Text className="text-xs text-muted-foreground">Lisbon</Text>
<Text className="pt-1 text-sm font-medium">{arrive}</Text>
</View>
</View>
{/* Perforation — the torn-edge convention on real passes. */}
<View className="mx-4 border-t border-dashed border-border" />
{/* Passenger + seat block. */}
<View className="flex-row justify-between px-5 py-4">
<View>
<Text className="text-[10px] uppercase text-muted-foreground">
Passenger
</Text>
<Text className="text-sm font-semibold">{passenger}</Text>
</View>
<View className="items-end">
<Text className="text-[10px] uppercase text-muted-foreground">
Seat
</Text>
<Text className="text-xl font-bold">{seat}</Text>
</View>
<View className="items-end">
<Text className="text-[10px] uppercase text-muted-foreground">
Gate
</Text>
<Text className="text-xl font-bold">{gate}</Text>
</View>
</View>
{/* Barcode — variable-width stripes like a real 1D code. */}
<View className="items-center px-5 pb-5">
<View className="h-16 w-full flex-row items-stretch justify-center gap-[1px]">
{barcode.map((w, i) => (
<View
key={i}
className="bg-foreground"
style={{ width: w }}
accessibilityElementsHidden
/>
))}
</View>
<Text className="pt-2 text-[10px] tracking-widest text-muted-foreground" style={{ fontFamily: MONO, fontVariant: ["tabular-nums"] }}>
{flight.replace(" ", "")} · {seat} · SFO→LIS
</Text>
</View>
</Mount>
{/* Add-to-wallet affordance — where real passes go. */}
<Mount delay={60} className="mx-4 mt-3">
<Button
variant="secondary"
haptic="light"
onPress={() => haptics.light()}
accessibilityLabel="Add to Wallet"
className="w-full"
>
<Icon name="download" size="sm" color="foreground" />
<Text className="ml-1.5">Add to Wallet</Text>
</Button>
</Mount>
{/* Flight details — the logistics block that rides with the pass. */}
<Mount delay={120} className="mx-4 mt-4 gap-2.5 overflow-hidden rounded-3xl bg-white p-5 dark:bg-[#1A1A20]">
{[
{ label: "Boarding", value: "7:45 PM" },
{ label: "Terminal", value: "2 · Door B" },
{ label: "Duration", value: "11h 25m" },
].map((r) => (
<View key={r.label} className="flex-row justify-between">
<Text className="text-sm text-muted-foreground">{r.label}</Text>
<Text className="text-sm font-semibold">{r.value}</Text>
</View>
))}
</Mount>
</ScrollView>
</SafeAreaView>
</World>
);
}
BoardingPassScreen.displayName = "BoardingPassScreen";
```
Screen: https://lumioui.dev/screens/travel-boarding-pass