# DOMoffers Bloom Native

`@domoffers/bloom-native` is the implementation-ready React Native design system extracted from the approved Bloom Selection prototype. It targets Expo SDK 57, React Native 0.86, React 19.2, iOS, Android, and React Native Web.

The package contains source TypeScript so it can live directly in the future app workspace. It does not contain application routing, networking, authentication, or state management.

The visual web catalog is available at `https://domoffers.691202.xyz/design-system.html`. It mirrors every public component family, all twenty-four typed icons, the token palette, and the important interactive states. Use it alongside `examples/ComponentGallery.tsx`: the website is the product-review surface, while the Expo gallery is the native regression fixture.

## Install in an Expo app

Place this folder at `packages/bloom-native`, then add it to the app:

```sh
npm install ./packages/bloom-native
npx expo install react-native-svg expo-font expo-splash-screen
```

`react-native-svg` renders the package’s own Bloom stroke icons consistently on iOS, Android, and web. This avoids binding the product language to a third-party icon font.

## Load Urbanist

The package expects these exact font-family keys:

```tsx
import { useFonts } from "expo-font";

const [loaded] = useFonts({
  Urbanist_400Regular: require("@domoffers/bloom-native/assets/fonts/urbanist-400.ttf"),
  Urbanist_500Medium: require("@domoffers/bloom-native/assets/fonts/urbanist-500.ttf"),
  Urbanist_600SemiBold: require("@domoffers/bloom-native/assets/fonts/urbanist-600.ttf"),
  Urbanist_700Bold: require("@domoffers/bloom-native/assets/fonts/urbanist-700.ttf"),
});
```

The four exact font files ship inside the package. In production, embed them with Expo Font’s config plugin for immediate native availability, and use `useFonts` where React Native Web support is required.

## Token contract

- `palette`: raw Bloom colors. Use only for brand-specific illustration or exceptional composition.
- `colors`: semantic roles used by components: background, surface, text, action, success, highlight, urgent, focus, and skeleton.
- `spacing`: the fixed `4 / 8 / 12 / 16 / 20 / 24 / 48` rhythm.
- `radii`: the named `10 / 12 / 13 / 14 / 16 / 20 / 24 / 26 / 33 / 38 / circle` shape system.
- `typography`: Urbanist roles with explicit size, line height, tracking, and font family.
- `sizes`: 44-point minimum touch targets plus canonical input, action, navigation, category, and signal sizes.
- `shadows`: platform-aware Air, Card, and Dock elevation levels.
- `motion`: feedback, exit, quick, screen, reveal, and spring timings. Reduced-motion handling is built into `Skeleton`; screen transitions should apply the same policy at app level.

Do not add raw colors, spacing, radii, or type sizes inside application screens. Add a reviewed semantic token here when the existing vocabulary truly cannot express the new need.

Small labels and icons on `palette.coral` use Carbon through `colors.actionText`; white does not provide sufficient contrast at this size. Error and urgent copy on pale surfaces uses `palette.coralInk` through `colors.urgentText`.

## Components

| Component | Purpose | Required behavioral contract |
| --- | --- | --- |
| `BloomButton` | Primary, secondary, and ghost actions | 50-point primary/secondary height, loading and disabled semantics |
| `IconButton` | Toolbar and compact actions | Required spoken label and 44 × 44 target |
| `FavoriteButton` | Canonical one-tap offer save action | Controlled `saved` state, selected/busy accessibility state, separate from offer opening |
| `Badge` | Short status or match label | Always includes text; icon and color are redundant cues |
| `Chip` | Filters and segments | Controlled selected state and 44-point target |
| `TextField` | Bloom form input | Visible label, focus/error treatment, hint/error announcement |
| `ProductAutocomplete` | Alert product targeting | Controlled query and expansion; normalized selections expose product and brand, while the optional free containing-text target is enabled only by similar-product mode |
| `SingleRegionSelect` | Alert territory targeting | Radio semantics and exactly one selected region per alert |
| `ToggleRow` | Alerts and settings | Entire row toggles; switch is hidden from the accessibility tree to prevent duplicate controls |
| `OfferSearchAutocomplete` | Home and Search query entry | Controlled free-text input; matching active offers sort first and every suggestion exposes lifecycle, product, brand, retailer and price |
| `CategoryRail` | Ribbon category discovery | Real product imagery in 56-point orbs; selected orb grows to 64 with a coral boundary |
| `OfferSignal(s)` | Discount, loyalty-card condition, expiry | Never use the former ambiguous “Prix vérifié” claim |
| `OfferCard` | Discovery, search, and active Favorites | Offer press and favorite press are sibling controls, never nested |
| `SelectionOfferGrid` | Home and Favorites offer collections | Derives Petal/Acid checkerboard, left/right rotation, overlap, and saved state from item position |
| `HeroOfferCard` | Home’s primary offer | Selection’s tilted Blush slab, counter-rotated content, conditions, detail affordance, and direct favorite |
| `FilterButton` / `FilterPanel` / `FilterChipGroup` | Search refinement | Controlled disclosure and selected options with centered icon treatment |
| `SearchResultCard` | Search result list | Selection result geometry with independent favorite and visible offer conditions |
| `OfferDetailMedia` / `OfferFacts` / `ProofNote` / `OfferActionDock` | Offer-detail composition | Keeps media, factual conditions, source proof, and primary actions reusable and separately accessible |
| `CatalogueFeatureCard` / `RetailerRow` | Catalogue discovery | Selection’s photographic feature and compact retailer list rows |
| `SegmentedControl` / `FavoriteOfferSections` | Saved-content organization | Keeps active offers first and recently expired offers at the top of the expired section |
| `AlertSummaryCard` / `AlertRow` | Alert overview and controls | Exposes count, territory, criteria, and a whole-row accessible switch |
| `ProfileIdentityCard` / `ProfileMenuRow` / `ConsentRow` | Account and consent composition | Keeps identity, navigation, and explicit checkbox semantics reusable |
| `ComparisonCard` | Exact or similar offer result | Effective normalized price and difference remain visible with match type and retailer |
| `ComparisonPreviewCard` / `ComparisonPreviewRail` | Offer-detail quick comparison | Compact horizontal cards; rail enforces the five-result preview limit |
| `PriceTruthCard` | Offer detail price explanation | Checkout price, reward, and effective price remain separate |
| `ExpiredOfferCard` | Historical Favorites item | Muted image, struck historical price, explicit expiry date, removable favorite |
| `BottomNav` | Five primary destinations | Controlled active item, tab roles, bottom inset supplied by the app shell |
| `AppToolbar` / `SubpageToolbar` / `ScreenHeader` | Screen structure | Navigation callbacks remain router-agnostic |
| `Skeleton` and composites | Remote data placeholders | Decorative to assistive tech, motion-reduced automatically |
| `EmptyState` | Empty collections and result sets | Optional focused recovery action |

## Canonical data and favorites

`OfferSummary` is the single offer-card input. `ComparisonOffer` extends it with match type, checkout price, reward, effective price, normalized unit price, and price difference.

Every offer component requires an `onToggleFavorite` callback; the API intentionally makes it difficult to ship an offer surface without the direct favorite action. `partitionFavoriteOffers()` returns active items first and expired items sorted by `expiredAt` descending.

```tsx
const groups = partitionFavoriteOffers(favorites);

groups.active.map(renderActiveOffer);
groups.expired.map(renderExpiredOffer);
```

Dates in `expiredAt` must be ISO `YYYY-MM-DD` values. Display copy belongs in `expiredLabel` until application localization provides formatted dates.

## Composition rules

- Use `20` points for normal screen gutters and reserve `106` points below scroll content when the bottom navigation overlays it.
- Put the Home hero and offer lists inside the same scroll view; horizontal rails must expose part of the next card.
- Render two-column offer collections with `SelectionOfferGrid`; do not assign card colors or rotation in screen code.
- Keep the five-item comparison preview at the bottom of Offer Detail and route its “See all” action to the dedicated comparison screen.
- Compare by effective normalized unit price after rewards. Exact matches may receive stronger emphasis but must not bypass price order.
- Public browsing stays available without a session. Favorite and alert mutations may route guests to authentication while preserving their intended action.
- Render skeletons for remote regions only. Keep screen identity, back navigation, filters, and local forms immediately usable.

## Validation

From this folder, after installing dependencies:

```sh
npm run typecheck
```

Use `examples/ComponentGallery.tsx` as the initial Expo component-catalog screen and visual regression fixture. Add React Native Testing Library coverage for press behavior, accessibility state, favorite separation, and expired sorting when the application repository is bootstrapped.
