Polish ComparisonPage mobile cards + page layout

- ComparisonTabCard: width 210 → 235; recommended badge switched to
  filled brand + StarRoundedIcon matching the desktop
  ComparisonColumnCard treatment; removed glow + active glow shadow in
  favour of the standard shadow-sm; border colour brand-500 → brand-600;
  pt 2.4 → 3.5.
- ComparisonPackageCard: verified badge replaced with inline
  VerifiedOutlinedIcon to the left of the provider name (matches
  desktop pattern); warm tint confined to the header (Card body now
  explicitly white); 2px brand-600 border when recommended; header
  padding px 2.5 → 3, pt 2.5 → 3, pb 2 → 4; spacing pass across the
  provider identity / package info / sections groups — Divider my
  1.5 → 3, section mb 3 → 5, item py 1.5 → 2, heading→first-item 1.5
  → 2.5.
- ComparisonPage mobile: Divider between page header and tab rail;
  "Choose a package to view" h2 heading (user-centric copy), used as
  aria-labelledby for the tablist; dot indicator below the rail
  (8px grey, active 24×8 brand-600 pill) — aria-hidden and tabIndex=-1
  so the tab rail above remains the canonical accessible navigation.

Also leaves the Figma capture script in preview-head.html for future
page captures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 15:52:07 +10:00
parent f146bb0f81
commit 312a77aeb9
5 changed files with 208 additions and 55 deletions

View File

@@ -9,7 +9,6 @@ import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined';
import type { SxProps, Theme } from '@mui/material/styles';
import { Typography } from '../../atoms/Typography';
import { Button } from '../../atoms/Button';
import { Badge } from '../../atoms/Badge';
import { Divider } from '../../atoms/Divider';
import { Card } from '../../atoms/Card';
import type { ComparisonPackage, ComparisonCellValue } from '../../organisms/ComparisonTable';
@@ -125,12 +124,21 @@ export const ComparisonPackageCard = React.forwardRef<HTMLDivElement, Comparison
<Card
ref={ref}
variant="outlined"
selected={pkg.isRecommended}
padding="none"
sx={[
{
overflow: 'hidden',
boxShadow: 'var(--fa-shadow-sm)',
// Body defaults to white; only the header carries the warm/subtle
// tint so the tint signals "provider" rather than washing the
// whole card.
bgcolor: 'background.paper',
// Match the desktop ComparisonColumnCard recommended treatment:
// explicit 2px brand-600 border (same as Card's selected state,
// but without the warm background wash that `selected` applies).
...(pkg.isRecommended && {
border: '2px solid var(--fa-color-brand-600)',
}),
},
...(Array.isArray(sx) ? sx : [sx]),
]}
@@ -158,31 +166,38 @@ export const ComparisonPackageCard = React.forwardRef<HTMLDivElement, Comparison
bgcolor: pkg.isRecommended
? 'var(--fa-color-surface-warm)'
: 'var(--fa-color-surface-subtle)',
px: 2.5,
pt: 2.5,
pb: 2,
px: 3,
pt: 3,
pb: 4,
}}
>
{/* Verified badge */}
{pkg.provider.verified && (
<Badge
color="brand"
variant="soft"
size="small"
icon={<VerifiedOutlinedIcon sx={{ fontSize: 14 }} />}
sx={{ mb: 1 }}
>
Verified
</Badge>
)}
{/* Provider name */}
<Typography variant="label" sx={{ fontWeight: 600, display: 'block', mb: 0.5 }}>
{pkg.provider.name}
</Typography>
{/* Provider name with optional inline verified icon (matches desktop
ComparisonColumnCard treatment) */}
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 0.75,
mb: 1.25,
}}
>
{pkg.provider.verified && (
<VerifiedOutlinedIcon
sx={{
fontSize: 16,
color: 'var(--fa-color-brand-600)',
flexShrink: 0,
}}
aria-label="Verified provider"
/>
)}
<Typography variant="label" sx={{ fontWeight: 600 }}>
{pkg.provider.name}
</Typography>
</Box>
{/* Location + Rating */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1.5 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.25 }}>
<LocationOnOutlinedIcon sx={{ fontSize: 14, color: 'text.secondary' }} aria-hidden />
<Typography variant="caption" color="text.secondary">
@@ -203,18 +218,22 @@ export const ComparisonPackageCard = React.forwardRef<HTMLDivElement, Comparison
)}
</Box>
<Divider sx={{ mb: 1.5 }} />
<Divider sx={{ my: 3 }} />
{/* Package name + price */}
<Typography variant="h5" component="p">
{pkg.name}
</Typography>
<Typography variant="caption" color="text.secondary">
Total package price
</Typography>
<Typography variant="h3" sx={{ color: 'primary.main', fontWeight: 700 }}>
{formatPrice(pkg.price)}
</Typography>
{/* Package info group — name, label, price stacked with small internal gap */}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75 }}>
<Typography variant="h5" component="p">
{pkg.name}
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.25 }}>
<Typography variant="caption" color="text.secondary">
Total package price
</Typography>
<Typography variant="h3" sx={{ color: 'primary.main', fontWeight: 700 }}>
{formatPrice(pkg.price)}
</Typography>
</Box>
</Box>
<Button
variant={pkg.provider.verified ? 'contained' : 'soft'}
@@ -222,14 +241,14 @@ export const ComparisonPackageCard = React.forwardRef<HTMLDivElement, Comparison
size="medium"
fullWidth
onClick={() => onArrange(pkg.id)}
sx={{ mt: 2 }}
sx={{ mt: 3 }}
>
{pkg.provider.verified ? 'Make Arrangement' : 'Make Enquiry'}
</Button>
</Box>
{/* Sections — with left accent borders on headings */}
<Box sx={{ px: 2.5, py: 2.5 }}>
<Box sx={{ px: 2.5, pt: 3.5, pb: 3 }}>
{pkg.itemizedAvailable === false ? (
<Box sx={{ textAlign: 'center', py: 3 }}>
<Typography variant="body2" color="text.secondary" sx={{ fontStyle: 'italic' }}>
@@ -238,15 +257,14 @@ export const ComparisonPackageCard = React.forwardRef<HTMLDivElement, Comparison
</Box>
) : (
pkg.sections.map((section, sIdx) => (
<Box key={section.heading} sx={{ mb: sIdx < pkg.sections.length - 1 ? 3 : 0 }}>
<Box key={section.heading} sx={{ mb: sIdx < pkg.sections.length - 1 ? 5 : 0 }}>
{/* Section heading with left accent */}
<Box
sx={{
borderLeft: '3px solid',
borderLeftColor: 'var(--fa-color-brand-500)',
pl: 1.5,
mb: 1.5,
mt: sIdx > 0 ? 1 : 0,
mb: 2.5,
}}
>
<Typography variant="h6" component="h3">
@@ -262,7 +280,7 @@ export const ComparisonPackageCard = React.forwardRef<HTMLDivElement, Comparison
alignItems: 'center',
justifyContent: 'space-between',
gap: 2,
py: 1.5,
py: 2,
borderBottom: '1px solid',
borderColor: 'divider',
}}

View File

@@ -1,5 +1,6 @@
import React from 'react';
import Box from '@mui/material/Box';
import StarRoundedIcon from '@mui/icons-material/StarRounded';
import type { SxProps, Theme } from '@mui/material/styles';
import { Typography } from '../../atoms/Typography';
import { Badge } from '../../atoms/Badge';
@@ -58,12 +59,15 @@ export const ComparisonTabCard = React.forwardRef<HTMLDivElement, ComparisonTabC
...(Array.isArray(sx) ? sx : [sx]),
]}
>
{/* Recommended badge in normal flow — overlaps card via negative mb */}
{/* Recommended badge in normal flow — overlaps card via negative mb.
Matches the desktop ComparisonColumnCard styling (filled brand +
star icon) for consistency between surfaces. */}
{pkg.isRecommended ? (
<Badge
color="brand"
variant="soft"
variant="filled"
size="small"
icon={<StarRoundedIcon sx={{ fontSize: 14 }} />}
sx={{
mb: '-10px',
zIndex: 1,
@@ -89,21 +93,18 @@ export const ComparisonTabCard = React.forwardRef<HTMLDivElement, ComparisonTabC
onClick={onClick}
interactive
sx={{
width: 210,
width: 235,
cursor: 'pointer',
boxShadow: 'var(--fa-shadow-sm)',
...(pkg.isRecommended && {
borderColor: 'var(--fa-color-brand-500)',
boxShadow: '0 0 12px rgba(186, 131, 78, 0.3)',
borderColor: 'var(--fa-color-brand-600)',
}),
...(isActive && {
boxShadow: pkg.isRecommended
? '0 0 14px rgba(186, 131, 78, 0.4)'
: 'var(--fa-shadow-md)',
boxShadow: 'var(--fa-shadow-md)',
}),
}}
>
<Box sx={{ px: 2, pt: 2.4, pb: 2 }}>
<Box sx={{ px: 2, pt: 3.5, pb: 2 }}>
<Typography
variant="labelSm"
sx={{