Add ProviderCard molecule — first molecule in design system
First molecule component. Listing card for funeral providers on the provider select screen (map + scrollable list layout). - Verified providers: hero image, 48px logo overlay, "Trusted Partner" badge, name, location, reviews, capability badge, footer with price - Unverified providers: text-only with same content alignment - 7 component tokens (image height, logo size, footer/content spacing) - Composes Card (interactive, padding="none") + Badge + Typography - Capability badges accept arbitrary label + colour (categories may change) - Footer bar with warm brand.100 bg, "Packages from $X >" - 9 Storybook stories including mixed list layout demo - Decisions D026-D030 logged Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,9 @@ duplicates) and MUST update it after completing one.
|
||||
| Component | Status | Composed of | Notes |
|
||||
|-----------|--------|-------------|-------|
|
||||
| FormField | planned | Input + Typography (label) + Typography (helper) | Standard form field with label and validation |
|
||||
| PriceCard | planned | Card + Typography + Badge + Button | Service pricing display |
|
||||
| ProviderCard | review | Card + Typography + Badge | Provider listing card. Verified: image + logo + "Trusted Partner" badge. Unverified: text-only. Capability badges (arbitrary label + colour). Footer with "Packages from $X >". 7 component tokens. |
|
||||
| VenueCard | planned | Card + Typography + Badge | Venue listing card. Always has photo, location, capacity, price. Simpler than ProviderCard. |
|
||||
| MapCard | planned | Card + Typography + Badge | Compact horizontal map popup card. Deferred until map integration. |
|
||||
| ServiceOption | planned | Card + Typography + Chip + Icon | Selectable service item |
|
||||
| SearchBar | planned | Input + Icon + Button | Search with submit |
|
||||
| StepIndicator | planned | Typography + Badge + Divider | Multi-step flow progress |
|
||||
|
||||
@@ -219,3 +219,43 @@ contradict a previous one.
|
||||
**Rationale:** Leading icons (email, phone, dollar) are essential for FA's arrangement forms. Success state provides positive feedback after validation. The Figma only had trailing icon, but leading icons are a near-universal production need. The `startIcon`/`endIcon` props are simpler than MUI's `InputAdornment` pattern while remaining compatible with raw adornments via `startAdornment`/`endAdornment`.
|
||||
**Affects:** Input component API, InputAdornment usage, Storybook stories
|
||||
**Alternatives considered:** Only supporting MUI's raw adornment API — rejected as too verbose for the common case. The convenience props are ergonomic while the raw props remain available for complex cases (e.g., password toggle with IconButton).
|
||||
|
||||
### D026 — ProviderCard establishes the molecule pattern
|
||||
**Date:** 2026-03-25
|
||||
**Category:** architecture
|
||||
**Decision:** ProviderCard is the first molecule. Molecules compose existing atoms via `sx` props — no MUI theme overrides. All styling from token CSS variables and theme accessors.
|
||||
**Rationale:** Keeps molecules lightweight and composable. Theme overrides are reserved for atoms where global consistency matters. Molecules are higher-level compositions with more context-specific layouts.
|
||||
**Affects:** All future molecules (VenueCard, MapCard, ServiceOption, etc.)
|
||||
**Alternatives considered:** Adding MuiProviderCard theme overrides — rejected as molecules are page-specific compositions, not reusable primitives.
|
||||
|
||||
### D027 — ProviderCard image is a URL string, not a ReactNode
|
||||
**Date:** 2026-03-25
|
||||
**Category:** component
|
||||
**Decision:** `imageUrl` prop accepts a string rendered as CSS `background-image` with `cover`. Logo is also a URL string rendered as `<img>`.
|
||||
**Rationale:** Simpler API for the common case (CDN URLs). CSS background-image handles aspect ratio cropping automatically. `<img>` for logo supports `alt` text natively for accessibility.
|
||||
**Affects:** ProviderCard, VenueCard, MapCard APIs
|
||||
**Alternatives considered:** ReactNode for image — rejected as over-flexible. A ReactNode slot would allow video/carousel but adds API complexity for a feature we don't need yet.
|
||||
|
||||
### D028 — Logo is 48px (not 75px from Figma)
|
||||
**Date:** 2026-03-25
|
||||
**Category:** component
|
||||
**Decision:** Logo overlay is 48px diameter, not the 75px shown in Figma.
|
||||
**Rationale:** On a ~340-400px card in a list panel, a 75px logo dominates the content area. 48px provides clear brand visibility while leaving sufficient space for the provider name and meta row.
|
||||
**Affects:** providerCard.logo.size token
|
||||
**Alternatives considered:** 75px from Figma — too large for the card width. 64px — still large relative to content.
|
||||
|
||||
### D029 — Footer is built into ProviderCard, not a slot
|
||||
**Date:** 2026-03-25
|
||||
**Category:** component
|
||||
**Decision:** The "Packages from $X >" footer is rendered via a `startingPrice` number prop, not a children/slot pattern.
|
||||
**Rationale:** The footer is structurally identical across all provider cards — warm background, "Packages from" text, price, chevron. Only the price value changes. A slot would add API complexity for no flexibility gain. If footer is omitted (no startingPrice), the bar is simply absent.
|
||||
**Affects:** ProviderCard API, VenueCard may use a similar pattern
|
||||
**Alternatives considered:** children/render prop for footer — rejected as over-engineered for a fixed layout.
|
||||
|
||||
### D030 — Verified is an explicit boolean, not derived from imageUrl
|
||||
**Date:** 2026-03-25
|
||||
**Category:** component
|
||||
**Decision:** `verified` boolean prop controls the visual treatment independently from `imageUrl`/`logoUrl`.
|
||||
**Rationale:** A provider could be verified but have a broken/missing image URL. The boolean is the source of truth from the business layer. Image and logo are only rendered when `verified` is also true, preventing accidental display of unverified providers with images.
|
||||
**Affects:** ProviderCard API, data model expectations
|
||||
**Alternatives considered:** Deriving verified from imageUrl presence — rejected as it couples business logic (partner status) to content availability (image uploaded).
|
||||
|
||||
@@ -496,6 +496,48 @@ Each entry follows this structure:
|
||||
- **Planned (5 organisms):** ServiceSelector, PricingTable, ArrangementForm, Navigation, Footer
|
||||
|
||||
**Next steps:**
|
||||
- User to review Switch and Radio in Storybook
|
||||
- Begin PriceCard molecule
|
||||
- ~~User to review Switch and Radio in Storybook~~ ✓ Approved
|
||||
- ~~Begin PriceCard molecule~~ ✓ Replaced with ProviderCard (see below)
|
||||
- Address P2 audit issues in a future cleanup pass
|
||||
|
||||
### Session 2026-03-25l — ProviderCard molecule (first molecule)
|
||||
|
||||
**Agent(s):** Claude Opus (via conversation)
|
||||
|
||||
**Work completed:**
|
||||
- Reviewed 3 Figma designs: Provider Cards (5503:44422), Service Venue (2997:83018), Map Pin Card (5369:140263)
|
||||
- Captured business context: verified vs unverified providers, Parsons partner strategy, map + list layout
|
||||
- Replaced generic "PriceCard" in registry with 3 specific molecules: ProviderCard, VenueCard, MapCard
|
||||
- Saved business context to auto-memory (project_listing_cards.md)
|
||||
- Created plan via /plan mode — component API, card structure, token plan, story plan
|
||||
- Created providerCard component tokens (`tokens/component/providerCard.json`): image.height, logo.size, footer.background/paddingX/paddingY, content.padding/gap — 7 tokens
|
||||
- Built ProviderCard molecule (`src/components/molecules/ProviderCard/ProviderCard.tsx`):
|
||||
- Composes Card (interactive, padding="none") + Badge + Typography
|
||||
- Verified variant: hero image (CSS bg-image cover), logo overlay (48px circle, absolute positioned, overlaps content), "Trusted Partner" badge (filled brand), capability badge
|
||||
- Unverified variant: text-only, same content area + footer
|
||||
- Footer bar: warm brand.100 bg, "Packages from $X" + ChevronRight
|
||||
- Accessibility: aria-label on reviews, aria-hidden on decorative icons, alt text on logo
|
||||
- All values from token CSS variables, no hardcoded hex
|
||||
- Created 9 Storybook stories: Default, VerifiedProvider, UnverifiedProvider, ListLayout (mixed), CapabilityVariants, EdgeCases, Responsive, OnDifferentBackgrounds, InteractiveDemo
|
||||
- Preflight passed all 5 checks
|
||||
- Logged D026-D030 in decisions log
|
||||
|
||||
**Decisions made:**
|
||||
- D026: Molecules compose atoms via sx — no MUI theme overrides
|
||||
- D027: Image as URL string (CSS bg-image), logo as URL string (<img>)
|
||||
- D028: Logo 48px (not 75px Figma) for card width proportion
|
||||
- D029: Footer built in via startingPrice prop, not a slot
|
||||
- D030: verified is explicit boolean, not derived from imageUrl
|
||||
|
||||
**Component status at end of session:**
|
||||
- **Done (7):** Button, Typography, Input, Card, Badge, Chip, Switch, Radio
|
||||
- **Review (1 molecule):** ProviderCard
|
||||
- **Planned (7 atoms):** IconButton, Icon, Avatar, Divider, ColourToggle, Slider, Link
|
||||
- **Planned (5 molecules):** VenueCard, MapCard, ServiceOption, SearchBar, StepIndicator, FormField
|
||||
- **Planned (5 organisms):** ServiceSelector, PricingTable, ArrangementForm, Navigation, Footer
|
||||
|
||||
**Next steps:**
|
||||
- User to review ProviderCard in Storybook — especially ListLayout story for verified/unverified alignment
|
||||
- Build VenueCard molecule (simpler — always has photo, location, capacity, price)
|
||||
- Consider MapCard as a deferred item until map integration
|
||||
- Address P2 audit issues in a future cleanup pass
|
||||
|
||||
@@ -264,3 +264,17 @@ the correct token for any design property.
|
||||
|-----------|-----------|---------|-------------|
|
||||
| radio.size.default | 20px | Radio | Outer circle size |
|
||||
| radio.dotSize.default | 10px | Radio | Inner selected dot size |
|
||||
|
||||
### ProviderCard
|
||||
|
||||
`tokens/component/providerCard.json`
|
||||
|
||||
| Token path | Value / Reference | Used by | Description |
|
||||
|-----------|-----------|---------|-------------|
|
||||
| providerCard.image.height | 180px | ProviderCard | Hero image fixed height |
|
||||
| providerCard.logo.size | 48px | ProviderCard | Logo circle diameter |
|
||||
| providerCard.footer.background | → color.brand.100 (#F7ECDF) | ProviderCard | Warm beige footer bar |
|
||||
| providerCard.footer.paddingX | → spacing.4 (16px) | ProviderCard | Footer horizontal padding |
|
||||
| providerCard.footer.paddingY | → spacing.3 (12px) | ProviderCard | Footer vertical padding |
|
||||
| providerCard.content.padding | → spacing.4 (16px) | ProviderCard | Content area padding |
|
||||
| providerCard.content.gap | → spacing.2 (8px) | ProviderCard | Gap between content rows |
|
||||
|
||||
Reference in New Issue
Block a user