Skip to content

Commit 90284d9

Browse files
committed
feat: introduce flat variant for SummaryCard with edge-to-edge layout
Add a new "flat" variant to the SummaryCard component, allowing for an edge-to-edge design without padding or elevation. This variant is ideal for seamless integration in grid layouts and carousels. Key changes: - Update SummaryCard styles to remove padding and border for the flat variant. - Enhance the component's props to include a variant option. - Add new stories to demonstrate the flat variant's use cases and layout. - Adjust button width behavior based on the selected variant. These updates improve the flexibility and usability of the SummaryCard component in various design contexts.
1 parent 169643d commit 90284d9

File tree

3 files changed

+147
-2
lines changed

3 files changed

+147
-2
lines changed

packages/ui/src/components/Card/SummaryCard.module.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
overflow: hidden;
77
}
88

9+
/* Flat variant - Remove border for seamless appearance */
10+
.summaryCard[data-variant='flat'] {
11+
border: none;
12+
}
13+
914
/* Image Section */
1015
.imageSection {
1116
margin: 0;
@@ -166,6 +171,11 @@
166171
gap: var(--ai-spacing-4); /* 8px between elements inside content */
167172
}
168173

174+
/* Flat variant - Remove horizontal padding for edge-to-edge layout */
175+
.summaryCard[data-variant='flat'] .contentSection {
176+
padding: 0;
177+
}
178+
169179
/* Title Row - Title/subtitle + badge on same row */
170180
.titleRow {
171181
display: flex;
@@ -256,10 +266,23 @@
256266
padding: 0 var(--ai-spacing-2); /* 0 vertical, 4px horizontal - creates 12px total with card padding */
257267
}
258268

269+
/* Flat variant - Remove horizontal padding for edge-to-edge layout */
270+
.summaryCard[data-variant='flat'] .buttonSection {
271+
padding: 0;
272+
}
273+
259274
.button {
260275
width: 100%;
261276
}
262277

278+
/* Desktop: Button auto-width when not full-width */
279+
@media (min-width: 768px) {
280+
.buttonSection[data-full-width='false'] .button {
281+
width: auto;
282+
min-width: 120px;
283+
}
284+
}
285+
263286
/* Error State */
264287
.errorContainer {
265288
padding: 0; /* Card padding handles spacing */

packages/ui/src/components/Card/SummaryCard.stories.tsx

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,79 @@ const SummaryCardsComponent: React.FC = () => {
450450
</div>
451451
</section>
452452

453+
{/* Flat Variant - Edge-to-Edge Layout */}
454+
<section style={{ marginBottom: '64px' }}>
455+
<header style={{ marginBottom: '24px' }}>
456+
<h2 style={{ marginBottom: '8px' }}>Flat Variant - Edge-to-Edge Layout</h2>
457+
<p style={{ color: 'var(--ai-color-text-secondary)', margin: 0, fontSize: '14px' }}>
458+
Zero-elevation, edge-to-edge layout with no padding. Perfect for seamless integration in grids, carousels, or when the container provides spacing. Uses variant="flat".
459+
</p>
460+
</header>
461+
462+
<div
463+
style={{
464+
display: 'grid',
465+
gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))',
466+
gap: '24px',
467+
alignItems: 'start',
468+
}}
469+
>
470+
<SummaryCard
471+
variant="flat"
472+
images={SAMPLE_IMAGES.restaurant}
473+
title="Flat Variant"
474+
subtitle="No padding or elevation"
475+
badge="9.2"
476+
description="Content extends to card edges for seamless layouts"
477+
buttonText="View Details"
478+
style={{ maxWidth: `${CARD_WIDTH}px` }}
479+
/>
480+
<SummaryCard
481+
variant="flat"
482+
images={[
483+
{ src: SAMPLE_IMAGES.pizza, alt: 'Pizza' },
484+
{ src: SAMPLE_IMAGES.pasta, alt: 'Pasta' },
485+
{ src: SAMPLE_IMAGES.salad, alt: 'Salad' },
486+
]}
487+
title="Grid with Flat"
488+
subtitle="Edge-to-edge grid"
489+
badge="9.0"
490+
description="Image grid and content aligned to edges"
491+
buttonText="Explore"
492+
style={{ maxWidth: `${CARD_WIDTH}px` }}
493+
/>
494+
<SummaryCard
495+
variant="flat"
496+
size="compact"
497+
imageAspectRatio="4/3"
498+
images={SAMPLE_IMAGES.restaurant}
499+
title="Flat + Compact"
500+
subtitle="Dense & seamless"
501+
badge="8.8"
502+
description="Combine flat variant with compact size"
503+
metadata={[
504+
{ label: '$$$', separator: '•' },
505+
{ label: 'Italian', separator: '•' },
506+
{ label: 'Open' },
507+
]}
508+
buttonText="Order Now"
509+
style={{ maxWidth: `${CARD_WIDTH}px` }}
510+
/>
511+
</div>
512+
513+
<div style={{ marginTop: '24px', background: 'var(--ai-color-bg-secondary)', border: '1px solid var(--ai-color-border)', borderRadius: '8px', padding: '16px' }}>
514+
<h3 style={{ fontSize: '14px', fontWeight: '600', marginBottom: '8px', color: 'var(--ai-color-text-primary)' }}>
515+
Use Cases
516+
</h3>
517+
<ul style={{ margin: 0, paddingLeft: '20px', fontSize: '13px', lineHeight: '1.6', color: 'var(--ai-color-text-secondary)' }}>
518+
<li><strong>Tight Grids:</strong> Cards in dense grid layouts where container provides spacing</li>
519+
<li><strong>Carousels:</strong> Seamless scrolling experiences without visual gaps</li>
520+
<li><strong>Mobile Optimization:</strong> Maximize content area on small screens</li>
521+
<li><strong>Custom Containers:</strong> When parent element handles padding and elevation</li>
522+
</ul>
523+
</div>
524+
</section>
525+
453526
{/* Discovery/Browse Mode - Compact Size (NEW - replaces DiscoveryCard) */}
454527
<section style={{ marginBottom: '64px' }}>
455528
<header style={{ marginBottom: '24px' }}>
@@ -915,6 +988,24 @@ const SummaryCardsComponent: React.FC = () => {
915988
default: "'default'",
916989
description: 'Badge styling variant',
917990
},
991+
{
992+
name: 'variant',
993+
type: "'default' | 'flat'",
994+
default: "'default'",
995+
description: 'Card variant - "flat" for edge-to-edge layout with no padding or elevation',
996+
},
997+
{
998+
name: 'size',
999+
type: "'default' | 'compact'",
1000+
default: "'default'",
1001+
description: 'Card size/density - "compact" for dense layouts',
1002+
},
1003+
{
1004+
name: 'imageAspectRatio',
1005+
type: "'auto' | '16/9' | '4/3' | '1/1'",
1006+
default: "'auto'",
1007+
description: 'Image aspect ratio for single images',
1008+
},
9181009
{
9191010
name: 'description',
9201011
type: 'string',
@@ -936,6 +1027,12 @@ const SummaryCardsComponent: React.FC = () => {
9361027
type: '() => void',
9371028
description: 'Button click handler',
9381029
},
1030+
{
1031+
name: 'buttonFullWidth',
1032+
type: 'boolean',
1033+
default: 'Auto (true for default, false for flat)',
1034+
description: 'Button full width on desktop. Auto-determined based on variant if not set.',
1035+
},
9391036
{
9401037
name: 'loading',
9411038
type: 'boolean',

packages/ui/src/components/Card/SummaryCard.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ export interface SummaryCardProps extends Omit<CardProps, 'children'> {
8383
*/
8484
badgeVariant?: BadgeVariant;
8585

86+
/**
87+
* Card variant
88+
* - "default": Standard card with padding and elevation
89+
* - "flat": Edge-to-edge layout with no padding or elevation
90+
* @default "default"
91+
*/
92+
variant?: 'default' | 'flat';
93+
8694
/**
8795
* Card size/density
8896
* - "default": Spacious layout, heading3 title, bodySmall subtitle
@@ -147,6 +155,15 @@ export interface SummaryCardProps extends Omit<CardProps, 'children'> {
147155
*/
148156
buttonDisabled?: boolean;
149157

158+
/**
159+
* Button full width on desktop.
160+
* - undefined: Auto (full-width for default variant, auto-width for flat variant)
161+
* - true: Full width on all screen sizes
162+
* - false: Auto width on desktop (min 120px), full width on mobile
163+
* @default undefined (auto based on variant)
164+
*/
165+
buttonFullWidth?: boolean;
166+
150167
// Phase 1: Critical Improvements (P0)
151168
/**
152169
* Loading state - shows skeleton UI
@@ -275,13 +292,15 @@ export const SummaryCard = React.forwardRef<HTMLDivElement, SummaryCardProps>((p
275292
subtitle,
276293
badge,
277294
badgeVariant = 'default',
295+
variant = 'default',
278296
size = 'default',
279297
imageAspectRatio = 'auto',
280298
description,
281299
metadata,
282300
buttonText,
283301
onButtonClick,
284302
buttonDisabled = false,
303+
buttonFullWidth,
285304
// Phase 1 props
286305
loading = false,
287306
loadingImageCount,
@@ -327,6 +346,10 @@ export const SummaryCard = React.forwardRef<HTMLDivElement, SummaryCardProps>((p
327346
const defaultLoadingCount = hasImages && !isSingleImage ? 3 : 1;
328347
const skeletonImageCount = loadingImageCount ?? defaultLoadingCount;
329348

349+
// Button width logic: auto-determine based on variant if not explicitly set
350+
// Default variant: full-width, Flat variant: auto-width (not full)
351+
const isButtonFullWidth = buttonFullWidth ?? variant === 'default';
352+
330353
// Helper to render badge/chip based on text length
331354
const renderBadge = () => {
332355
if (badge === undefined) return null;
@@ -351,9 +374,11 @@ export const SummaryCard = React.forwardRef<HTMLDivElement, SummaryCardProps>((p
351374
return (
352375
<Card
353376
ref={ref}
354-
padding={8}
377+
padding={variant === 'flat' ? 0 : 8}
378+
elevationLevel={variant === 'flat' ? 0 : cardProps.elevationLevel}
355379
className={cn(styles.summaryCard, className)}
356380
data-size={size}
381+
data-variant={variant}
357382
{...cardProps}
358383
>
359384
{/* Loading State - Single Image */}
@@ -593,7 +618,7 @@ export const SummaryCard = React.forwardRef<HTMLDivElement, SummaryCardProps>((p
593618

594619
{/* Action Button */}
595620
{hasButton && (
596-
<div className={styles.buttonSection}>
621+
<div className={styles.buttonSection} data-full-width={isButtonFullWidth}>
597622
<Button
598623
variant="primary"
599624
onClick={onButtonClick}

0 commit comments

Comments
 (0)