v0.1 public beta early access on GitHub
Home / Screens / health-med-store
Screen template

Med Store

Med Store screen.

pro Expo Go ✓ iosandroid gestures: tap haptics: light · selection
$ npx lumioui add health-med-store
import { MedStoreScreen } from "@/screens/health/med-store";
Registry dependencies1
Native packages installed6
@lumioui/ui@latest@lumioui/core@latestreact-native-svg@*react-native-safe-area-context@*react-native-reanimated@*nativewind@*
health/med-store.tsxsource · yours to edit
import { useState } from "react";
import { Image, ScrollView, View } from "react-native";
import Svg, { Path } from "react-native-svg";
import { SafeAreaView } from "react-native-safe-area-context";
import { Avatar, Button, Glass, Icon, Text, type IconName } from "@lumioui/ui";
import { cn, useHaptic } from "@lumioui/core";
import { Mount, Press, ROW_STAGGER, ROW_STAGGER_CAP } from "../shared";

export interface MedStoreScreenProps {
  onNext?: () => void;
}

const ACTIONS: { icon: IconName; label: string }[] = [
  { icon: "camera", label: "Upload" },
  { icon: "package", label: "Order" },
  { icon: "pin", label: "Pharmacy" },
  { icon: "clock", label: "Reminder" },
];

const MEDS: { name: string; price: string; tint: string; img: string }[] = [
  {
    name: "Women Multivitamin",
    price: "$29.00",
    tint: "#BFE9F1",
    img: "https://images.unsplash.com/photo-1587854692152-cbe660dbde88?w=700&q=80",
  },
  {
    name: "Calcium Core",
    price: "$24.50",
    tint: "#F6C4D5",
    img: "https://images.unsplash.com/photo-1471864190281-a93a3070b6de?w=700&q=80",
  },
  {
    name: "Omega-3",
    price: "$19.99",
    tint: "#FFE0B8",
    img: "https://images.unsplash.com/photo-1584308666744-24d5c474f2ae?w=700&q=80",
  },
];

function ActionPill({ icon, label, index }: { icon: IconName; label: string; index: number }) {
  const haptics = useHaptic();
  return (
    <Mount delay={140 + index * ROW_STAGGER} className="w-[48%]">
      <Press onPress={() => haptics.light()} className="w-full rounded-full bg-white p-1.5">
        <View className="flex-row items-center gap-3">
          <View className="h-10 w-10 items-center justify-center rounded-full bg-[#F4F5F9]">
            <Icon name={icon} size="md" color="#1C1C1E" />
          </View>
          <Text className="text-[15px] font-medium text-[#1C1C1E]">{label}</Text>
        </View>
      </Press>
    </Mount>
  );
}

function MedCard({ med, index, onAdd }: { med: (typeof MEDS)[number]; index: number; onAdd: () => void }) {
  const haptics = useHaptic();
  const [faved, setFaved] = useState(false);
  return (
    <Mount delay={220 + index * 90} className="mr-3 w-[310px]">
      <View className="overflow-hidden rounded-[30px] bg-white pb-5">
        <View className="mx-4 mt-4 h-56 overflow-hidden rounded-[22px]" style={{ backgroundColor: med.tint }}>
          <Image source={{ uri: med.img }} className="absolute inset-0" resizeMode="cover" accessibilityIgnoresInvertColors />
          <Press
            accessibilityRole="button"
            accessibilityLabel="Favorite"
            onPress={() => {
              haptics.light();
              setFaved((f) => !f);
            }}
            className="absolute right-3 top-3 h-9 w-9 items-center justify-center rounded-full bg-white"
          >
            <Icon name="heart" size="md" color={faved ? "#E2503B" : "#1C1C1E"} />
          </Press>
        </View>
        <View className="flex-row items-end justify-between px-5 pt-4">
          <View className="min-w-0 flex-1">
            <Text className="text-[16px] font-semibold text-[#1C1C1E]" numberOfLines={1}>{med.name}</Text>
            <Text className="mt-1 text-[22px] font-bold text-[#1C1C1E]">{med.price}</Text>
          </View>
          <Press
            accessibilityRole="button"
            accessibilityLabel="Add to cart"
            onPress={() => {
              haptics.light();
              onAdd();
            }}
            className="h-11 w-11 items-center justify-center rounded-full bg-[#1B1B26]"
          >
            <Icon name="plus" size="md" color="#BEF04C" />
          </Press>
        </View>
      </View>
    </Mount>
  );
}

export function MedStoreScreen({ onNext }: MedStoreScreenProps) {
  const haptics = useHaptic();
  const [cart, setCart] = useState(0);
  const complete = () => {
    haptics.success();
    onNext?.();
  };
  return (
    <View className="flex-1 bg-[#F4F5F9]">
      <SafeAreaView className="flex-1" edges={["top"]}>
        <Mount className="flex-row items-center gap-3 px-6 pt-2" delay={40}>
          <Avatar size="lg" fallback="MJ" source={{ uri: "https://i.pravatar.cc/150?img=11" }} />
          <View className="flex-1">
            <Text className="text-[15px] font-semibold text-[#1C1C1E]">Michael Johnson</Text>
            <View className="flex-row items-center gap-1">
              <Icon name="pin" size="sm" color="#8E8E93" />
              <Text className="text-[12px] text-[#8E8E93]">New York, NY</Text>
            </View>
          </View>
          <Press accessibilityRole="button" accessibilityLabel="Cart" onPress={() => haptics.light()} className="h-11 w-11 items-center justify-center rounded-full bg-white">
            <Icon name="package" size="md" color="#1C1C1E" />
            {cart > 0 ? (
              <View className="absolute -right-0.5 -top-0.5 h-5 min-w-5 items-center justify-center rounded-full bg-[#BEF04C] px-1">
                <Text className="text-[10px] font-bold text-[#1C1C1E]">{cart}</Text>
              </View>
            ) : (
              <View className="absolute right-2.5 top-2.5 h-2 w-2 rounded-full bg-[#BEF04C]" />
            )}
          </Press>
        </Mount>
        <ScrollView showsVerticalScrollIndicator={false} contentContainerClassName="pb-44">
          <Mount delay={80} className="mt-6 flex-row items-center justify-between px-6">
            <Text className="text-[30px] font-bold leading-[38px] text-[#1C1C1E]">
              {"Find Your\nMedicines Fast"}
            </Text>
            <Press
              accessibilityRole="button" accessibilityLabel="Search medicines"
              onPress={complete}
              className="h-14 w-14 items-center justify-center rounded-full bg-[#1C1C1E]"
            >
              <Icon name="search" size="lg" color="#FFFFFF" />
            </Press>
          </Mount>
          <View className="mt-6 flex-row flex-wrap justify-between gap-y-3 px-6">
            {ACTIONS.map((a, i) => (
              <ActionPill key={a.label} icon={a.icon} label={a.label} index={i} />
            ))}
          </View>
          <ScrollView
            horizontal
            showsHorizontalScrollIndicator={false}
            className="mt-7"
            contentContainerClassName="pl-6 pr-8"
          >
            {MEDS.map((m, i) => (
              <MedCard key={m.name} med={m} index={i} onAdd={() => setCart((c) => c + 1)} />
            ))}
          </ScrollView>
        </ScrollView>
        <View className="absolute inset-x-5 bottom-8">
          <Press
            accessibilityRole="button"
            accessibilityLabel="Scan"
            onPress={() => haptics.light()}
            className="absolute -top-7 left-1/2 z-10 h-[60px] w-[60px] -translate-x-1/2 items-center justify-center rounded-full bg-[#1C1C1E]"
          >
            <Svg width={22} height={22} viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" strokeWidth={2} strokeLinecap="round">
              <Path d="M4 8V6a2 2 0 0 1 2-2h2" />
              <Path d="M16 4h2a2 2 0 0 1 2 2v2" />
              <Path d="M20 16v2a2 2 0 0 1-2 2h-2" />
              <Path d="M8 20H6a2 2 0 0 1-2-2v-2" />
              <Path d="M5 12h14" />
            </Svg>
          </Press>
          <Glass effect="regular" className="flex-row items-center justify-around rounded-full px-2 py-3">
            <NavItem icon="home" label="Home" active />
            <NavItem icon="list" label="Orders" />
            <View className="w-14" />
            <NavItem icon="sparkles" label="AI Chat" />
            <NavItem icon="user" label="Profile" />
          </Glass>
        </View>
      </SafeAreaView>
    </View>
  );
}

function NavItem({ icon, label, active }: { icon: IconName; label: string; active?: boolean }) {
  const haptics = useHaptic();
  return (
    <Press onPress={() => haptics.light()} className="items-center gap-0.5 px-1">
      <Icon name={icon} size="md" color={active ? "#1C1C1E" : "#9C9CA6"} />
      <Text className={active ? "text-[11px] font-semibold text-[#1C1C1E]" : "text-[11px] text-[#9C9CA6]"}>
        {label}
      </Text>
    </Press>
  );
}
agent prompt — paste into Claude Code, Cursor, Devin…
Recreate the "Med Store" screen in React Native using LumioUI.

Goal: Med Store screen. 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 health-med-store
```

Or pull just the components: `npx lumioui add button`

Native packages:
- @lumioui/ui@latest
- @lumioui/core@latest
- react-native-svg@*
- react-native-safe-area-context@*
- react-native-reanimated@*
- 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.
- imported but not rendered directly — likely used through a sibling or as a utility

## 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/health/med-store.tsx` exporting MedStoreScreen. 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 { useState } from "react";
import { Image, ScrollView, View } from "react-native";
import Svg, { Path } from "react-native-svg";
import { SafeAreaView } from "react-native-safe-area-context";
import { Avatar, Button, Glass, Icon, Text, type IconName } from "@lumioui/ui";
import { cn, useHaptic } from "@lumioui/core";
import { Mount, Press, ROW_STAGGER, ROW_STAGGER_CAP } from "../shared";

export interface MedStoreScreenProps {
  onNext?: () => void;
}

const ACTIONS: { icon: IconName; label: string }[] = [
  { icon: "camera", label: "Upload" },
  { icon: "package", label: "Order" },
  { icon: "pin", label: "Pharmacy" },
  { icon: "clock", label: "Reminder" },
];

const MEDS: { name: string; price: string; tint: string; img: string }[] = [
  {
    name: "Women Multivitamin",
    price: "$29.00",
    tint: "#BFE9F1",
    img: "https://images.unsplash.com/photo-1587854692152-cbe660dbde88?w=700&q=80",
  },
  {
    name: "Calcium Core",
    price: "$24.50",
    tint: "#F6C4D5",
    img: "https://images.unsplash.com/photo-1471864190281-a93a3070b6de?w=700&q=80",
  },
  {
    name: "Omega-3",
    price: "$19.99",
    tint: "#FFE0B8",
    img: "https://images.unsplash.com/photo-1584308666744-24d5c474f2ae?w=700&q=80",
  },
];

function ActionPill({ icon, label, index }: { icon: IconName; label: string; index: number }) {
  const haptics = useHaptic();
  return (
    <Mount delay={140 + index * ROW_STAGGER} className="w-[48%]">
      <Press onPress={() => haptics.light()} className="w-full rounded-full bg-white p-1.5">
        <View className="flex-row items-center gap-3">
          <View className="h-10 w-10 items-center justify-center rounded-full bg-[#F4F5F9]">
            <Icon name={icon} size="md" color="#1C1C1E" />
          </View>
          <Text className="text-[15px] font-medium text-[#1C1C1E]">{label}</Text>
        </View>
      </Press>
    </Mount>
  );
}

function MedCard({ med, index, onAdd }: { med: (typeof MEDS)[number]; index: number; onAdd: () => void }) {
  const haptics = useHaptic();
  const [faved, setFaved] = useState(false);
  return (
    <Mount delay={220 + index * 90} className="mr-3 w-[310px]">
      <View className="overflow-hidden rounded-[30px] bg-white pb-5">
        <View className="mx-4 mt-4 h-56 overflow-hidden rounded-[22px]" style={{ backgroundColor: med.tint }}>
          <Image source={{ uri: med.img }} className="absolute inset-0" resizeMode="cover" accessibilityIgnoresInvertColors />
          <Press
            accessibilityRole="button"
            accessibilityLabel="Favorite"
            onPress={() => {
              haptics.light();
              setFaved((f) => !f);
            }}
            className="absolute right-3 top-3 h-9 w-9 items-center justify-center rounded-full bg-white"
          >
            <Icon name="heart" size="md" color={faved ? "#E2503B" : "#1C1C1E"} />
          </Press>
        </View>
        <View className="flex-row items-end justify-between px-5 pt-4">
          <View className="min-w-0 flex-1">
            <Text className="text-[16px] font-semibold text-[#1C1C1E]" numberOfLines={1}>{med.name}</Text>
            <Text className="mt-1 text-[22px] font-bold text-[#1C1C1E]">{med.price}</Text>
          </View>
          <Press
            accessibilityRole="button"
            accessibilityLabel="Add to cart"
            onPress={() => {
              haptics.light();
              onAdd();
            }}
            className="h-11 w-11 items-center justify-center rounded-full bg-[#1B1B26]"
          >
            <Icon name="plus" size="md" color="#BEF04C" />
          </Press>
        </View>
      </View>
    </Mount>
  );
}

export function MedStoreScreen({ onNext }: MedStoreScreenProps) {
  const haptics = useHaptic();
  const [cart, setCart] = useState(0);
  const complete = () => {
    haptics.success();
    onNext?.();
  };
  return (
    <View className="flex-1 bg-[#F4F5F9]">
      <SafeAreaView className="flex-1" edges={["top"]}>
        <Mount className="flex-row items-center gap-3 px-6 pt-2" delay={40}>
          <Avatar size="lg" fallback="MJ" source={{ uri: "https://i.pravatar.cc/150?img=11" }} />
          <View className="flex-1">
            <Text className="text-[15px] font-semibold text-[#1C1C1E]">Michael Johnson</Text>
            <View className="flex-row items-center gap-1">
              <Icon name="pin" size="sm" color="#8E8E93" />
              <Text className="text-[12px] text-[#8E8E93]">New York, NY</Text>
            </View>
          </View>
          <Press accessibilityRole="button" accessibilityLabel="Cart" onPress={() => haptics.light()} className="h-11 w-11 items-center justify-center rounded-full bg-white">
            <Icon name="package" size="md" color="#1C1C1E" />
            {cart > 0 ? (
              <View className="absolute -right-0.5 -top-0.5 h-5 min-w-5 items-center justify-center rounded-full bg-[#BEF04C] px-1">
                <Text className="text-[10px] font-bold text-[#1C1C1E]">{cart}</Text>
              </View>
            ) : (
              <View className="absolute right-2.5 top-2.5 h-2 w-2 rounded-full bg-[#BEF04C]" />
            )}
          </Press>
        </Mount>
        <ScrollView showsVerticalScrollIndicator={false} contentContainerClassName="pb-44">
          <Mount delay={80} className="mt-6 flex-row items-center justify-between px-6">
            <Text className="text-[30px] font-bold leading-[38px] text-[#1C1C1E]">
              {"Find Your\nMedicines Fast"}
            </Text>
            <Press
              accessibilityRole="button" accessibilityLabel="Search medicines"
              onPress={complete}
              className="h-14 w-14 items-center justify-center rounded-full bg-[#1C1C1E]"
            >
              <Icon name="search" size="lg" color="#FFFFFF" />
            </Press>
          </Mount>
          <View className="mt-6 flex-row flex-wrap justify-between gap-y-3 px-6">
            {ACTIONS.map((a, i) => (
              <ActionPill key={a.label} icon={a.icon} label={a.label} index={i} />
            ))}
          </View>
          <ScrollView
            horizontal
            showsHorizontalScrollIndicator={false}
            className="mt-7"
            contentContainerClassName="pl-6 pr-8"
          >
            {MEDS.map((m, i) => (
              <MedCard key={m.name} med={m} index={i} onAdd={() => setCart((c) => c + 1)} />
            ))}
          </ScrollView>
        </ScrollView>
        <View className="absolute inset-x-5 bottom-8">
          <Press
            accessibilityRole="button"
            accessibilityLabel="Scan"
            onPress={() => haptics.light()}
            className="absolute -top-7 left-1/2 z-10 h-[60px] w-[60px] -translate-x-1/2 items-center justify-center rounded-full bg-[#1C1C1E]"
          >
            <Svg width={22} height={22} viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" strokeWidth={2} strokeLinecap="round">
              <Path d="M4 8V6a2 2 0 0 1 2-2h2" />
              <Path d="M16 4h2a2 2 0 0 1 2 2v2" />
              <Path d="M20 16v2a2 2 0 0 1-2 2h-2" />
              <Path d="M8 20H6a2 2 0 0 1-2-2v-2" />
              <Path d="M5 12h14" />
            </Svg>
          </Press>
          <Glass effect="regular" className="flex-row items-center justify-around rounded-full px-2 py-3">
            <NavItem icon="home" label="Home" active />
            <NavItem icon="list" label="Orders" />
            <View className="w-14" />
            <NavItem icon="sparkles" label="AI Chat" />
            <NavItem icon="user" label="Profile" />
          </Glass>
        </View>
      </SafeAreaView>
    </View>
  );
}

function NavItem({ icon, label, active }: { icon: IconName; label: string; active?: boolean }) {
  const haptics = useHaptic();
  return (
    <Press onPress={() => haptics.light()} className="items-center gap-0.5 px-1">
      <Icon name={icon} size="md" color={active ? "#1C1C1E" : "#9C9CA6"} />
      <Text className={active ? "text-[11px] font-semibold text-[#1C1C1E]" : "text-[11px] text-[#9C9CA6]"}>
        {label}
      </Text>
    </Press>
  );
}
```

Screen: https://lumioui.dev/screens/health-med-store
iPhone 17 · iOS
9:41
Live preview — the real screen via Expo Web · open full ↗