Add package comparison feature: CompareBar, ComparisonTable, ComparisonPage
New components for side-by-side funeral package comparison: - CompareBar molecule: floating bottom pill with fraction badge (1/3, 2/3, 3/3), contextual copy, Compare CTA. For ProvidersStep and PackagesStep. - ComparisonTable organism: CSS Grid comparison with info card, floating verified badges, separate section tables (Essentials/Optionals/Extras) with left accent borders, row hover, horizontal scroll on narrow desktops, font hierarchy. - ComparisonPage: WizardLayout wide-form with Share/Print actions. Desktop shows ComparisonTable, mobile shows mini-card tab rail + single package card view. Recommended package as separate prop (D038). Also fixes PackageDetail: adds priceLabel pass-through (D039), updates stories to Essentials/Optionals/Extras section naming (D035). Decisions: D035-D039 logged. Audits: CompareBar 18/20, ComparisonTable 17/20. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -293,3 +293,43 @@ contradict a previous one.
|
||||
**Rationale:** P0/P1 are the issues that affect usability and accessibility. P2/P3 are cosmetic — not worth the risk of changing approved components. Interleaving ensures the foundation is solid before building on it, without dedicating entire sessions to review.
|
||||
**Affects:** Session workflow, CLAUDE.md startup procedure, docs/reference/retroactive-review-plan.md
|
||||
**Alternatives considered:** Dedicated review sessions — rejected as less efficient. Full P0-P3 fixes — rejected as too risky for approved components.
|
||||
|
||||
### D035 — Package sections standardised to Essentials / Optionals / Extras
|
||||
**Date:** 2026-04-06
|
||||
**Category:** component
|
||||
**Decision:** Package data uses three sections: **Essentials** (priced core items), **Optionals** (complimentary inclusions), **Extras** (additional-cost items after the total). Replaces the previous "Complimentary Items" naming.
|
||||
**Rationale:** Matches the real-world package structure from FA's provider data (see reference image). "Optionals" better communicates that these are included-but-not-mandatory items, while "Complimentary" is a price label on individual items, not a section name.
|
||||
**Affects:** PackageDetail stories, ComparisonTable sections, ComparisonPage mobile cards
|
||||
**Alternatives considered:** "Inclusions" instead of "Optionals" — rejected as it overlaps with Essentials (which are also inclusions).
|
||||
|
||||
### D036 — ComparisonCellValue uses discriminated union type
|
||||
**Date:** 2026-04-06
|
||||
**Category:** architecture
|
||||
**Decision:** Cell values in ComparisonTable use a tagged union type (`{ type: 'price' | 'allowance' | 'complimentary' | 'included' | 'poa' | 'unknown' | 'unavailable' }`) rather than flat optional props.
|
||||
**Rationale:** Ensures exhaustive pattern matching in CellValue renderer — the TypeScript compiler catches missing cases. Clearer than a flat `{ price?: number; priceLabel?: string; isAllowance?: boolean }` which has ambiguous combinations. Each value type maps to a distinct visual treatment.
|
||||
**Affects:** ComparisonTable, ComparisonPage mobile card view
|
||||
**Alternatives considered:** Reusing PackageLineItem from PackageDetail — rejected as it conflates "how data is stored" with "how data is displayed". The comparison needs explicit cell state (e.g. "unavailable" vs "unknown").
|
||||
|
||||
### D037 — Mobile comparison uses chip tabs, not horizontal scroll table
|
||||
**Date:** 2026-04-06
|
||||
**Category:** component
|
||||
**Decision:** ComparisonPage renders a chip-based tab rail + single card view on mobile, rather than a horizontally scrollable table.
|
||||
**Rationale:** Wide comparison tables on small screens create "hidden column" problems — users can't see all packages at once and may miss columns. Card view with tabs matches mental model of reviewing one option at a time. Lower cognitive load for FA's grief-sensitive audience. Tab rail provides quick switching. ARIA tablist/tabpanel semantics.
|
||||
**Affects:** ComparisonPage mobile layout
|
||||
**Alternatives considered:** Horizontal scroll table — rejected for poor usability on small screens. Accordion per package — rejected as it hides content behind extra taps.
|
||||
|
||||
### D038 — Recommended package is a separate prop, not mixed into packages array
|
||||
**Date:** 2026-04-06
|
||||
**Category:** architecture
|
||||
**Decision:** ComparisonPage accepts `recommendedPackage` as a separate prop from `packages`. The page merges it as the last column with `isRecommended: true`.
|
||||
**Rationale:** Keeps the user-selected array clean and unambiguous. The recommendation source is explicit (server-side logic). The page controls placement (always last column/tab). Prevents accidental removal of the recommended package by the user (no Remove button).
|
||||
**Affects:** ComparisonPage props, ComparisonTable isRecommended column
|
||||
**Alternatives considered:** Including recommended in the packages array with a flag — rejected as it mixes user selections with system recommendations.
|
||||
|
||||
### D039 — PackageLineItem gains priceLabel for consistency with LineItem
|
||||
**Date:** 2026-04-06
|
||||
**Category:** component
|
||||
**Decision:** Added `priceLabel?: string` to `PackageLineItem` interface in PackageDetail, passed through to LineItem molecule.
|
||||
**Rationale:** LineItem already supports `priceLabel` for custom price text ("Complimentary", "Price On Application", "TBC"). PackageDetail's type was missing this field, forcing workarounds. Adding it enables the Optionals section to display "Complimentary" labels and Extras to show "Price On Application".
|
||||
**Affects:** PackageDetail component + stories, any consumer of PackageLineItem type
|
||||
**Alternatives considered:** None — this was a straightforward type parity fix.
|
||||
|
||||
Reference in New Issue
Block a user