Add CardsIconGrid and CardsTextGrid Section Patterns (#3505)

* CardsIconGrid and CardsTextGrid + showcase pages

* cleaning up code

* more code clean up

* using the spacing tokens for values
This commit is contained in:
Calvin
2026-02-18 16:19:01 -08:00
committed by Calvin Jhunjhuwala
parent c8bed8b698
commit fe1aa0ecb1
19 changed files with 1083 additions and 29929 deletions

View File

@@ -0,0 +1,87 @@
// BDS CardsIconGrid Pattern Styles
// Brand Design System - Section with heading, description, and grid of CardTextIconCard
//
// Naming Convention: BEM with 'bds' namespace
// .bds-cards-icon-grid - Base section container
// .bds-cards-icon-grid__header - Header section (heading + description)
// .bds-cards-icon-grid__list - Grid list (ul as PageGrid.Row)
// =============================================================================
// Design Tokens (uses BDS spacing from _spacing.scss)
// =============================================================================
$bds-cards-icon-grid-padding-base: $bds-space-2xl;
$bds-cards-icon-grid-padding-md: $bds-space-3xl;
$bds-cards-icon-grid-padding-lg: $bds-space-4xl;
$bds-cards-icon-grid-header-gap-base: $bds-gap-header-sm;
$bds-cards-icon-grid-header-gap-md: $bds-gap-header-md;
$bds-cards-icon-grid-header-gap-lg: $bds-gap-header-lg;
$bds-cards-icon-grid-header-margin-base: $bds-gap-section-sm;
$bds-cards-icon-grid-header-margin-md: $bds-gap-section-md;
$bds-cards-icon-grid-header-margin-lg: $bds-gap-section-lg;
// =============================================================================
// Section Container
// =============================================================================
.bds-cards-icon-grid {
padding-top: $bds-cards-icon-grid-padding-base;
@include media-breakpoint-up(md) {
padding-top: $bds-cards-icon-grid-padding-md;
}
@include media-breakpoint-up(lg) {
padding-top: $bds-cards-icon-grid-padding-lg;
}
ul {
padding-left: 0;
list-style: none;
}
@include bds-theme-mode(dark) {
color: $white;
}
}
// =============================================================================
// Header Section
// =============================================================================
.bds-cards-icon-grid__header {
display: flex;
flex-direction: column;
gap: $bds-cards-icon-grid-header-gap-base;
margin-bottom: $bds-cards-icon-grid-header-margin-base;
@include media-breakpoint-up(md) {
gap: $bds-cards-icon-grid-header-gap-md;
margin-bottom: $bds-cards-icon-grid-header-margin-md;
}
@include media-breakpoint-up(lg) {
gap: $bds-cards-icon-grid-header-gap-lg;
margin-bottom: $bds-cards-icon-grid-header-margin-lg;
}
h2, p {
margin-bottom: 0;
}
}
// =============================================================================
// List Grid - Aspect Ratios
// =============================================================================
.bds-cards-icon-grid__list > li {
aspect-ratio: 3 / 2;
@include media-breakpoint-up(md) {
aspect-ratio: 4 / 3;
}
@include media-breakpoint-up(lg) {
aspect-ratio: 4 / 3;
}
}

View File

@@ -0,0 +1,66 @@
import React from 'react';
import clsx from 'clsx';
import { PageGrid, PageGridRow, PageGridCol } from 'shared/components/PageGrid/page-grid';
import { CardTextIconCard, CardTextIconCardProps } from 'shared/components/CardTextIcon';
export interface CardsIconGridProps {
/** Section heading (required) */
heading: string;
/** Optional description text */
description?: string;
/** Array of card data to display */
cards: CardTextIconCardProps[];
/** Additional CSS classes */
className?: string;
}
/**
* CardsIconGrid Component
*
* A section pattern with a header (heading + description) and a grid of
* CardTextIconCard components. Uses PageGrid.Row as="ul" with cards as
* PageGrid.Col as="li". Aspect ratios: sm 3:2, md/lg 4:3.
*
* @example
* <CardsIconGrid
* heading="Explore Tools"
* description="Choose a tool to get started"
* cards={[
* { icon: "/icons/docs.svg", heading: "Documentation", description: "..." },
* ]}
* />
*/
export const CardsIconGrid: React.FC<CardsIconGridProps> = ({
heading,
description,
cards,
className,
}) => {
return (
<PageGrid className={clsx('bds-cards-icon-grid', className)}>
<PageGridRow>
<PageGridCol className="bds-cards-icon-grid__header" span={{ base: 12, md: 6, lg: 8 }}>
<h2 className="h-md">{heading}</h2>
{description && <p className="body-l">{description}</p>}
</PageGridCol>
</PageGridRow>
<PageGridRow as="ul" className="bds-cards-icon-grid__list list-none pl-0">
{cards.map((card, index) => (
<CardTextIconCard
key={card.heading || index}
icon={card.icon}
iconAlt={card.iconAlt}
heading={card.heading}
description={card.description}
height={card.height}
width={card.width}
gridColSpan={{ base: 4, md: 4, lg: 4 }}
/>
))}
</PageGridRow>
</PageGrid>
);
};
export default CardsIconGrid;

View File

@@ -0,0 +1,53 @@
# CardsIconGrid Pattern
A section pattern with a header (heading + description) and a grid of CardTextIconCard components.
## Overview
CardsIconGrid mirrors the LinkTextDirectory header structure and renders cards in a responsive grid using PageGrid.Row as="ul" and CardTextIconCard with gridColSpan. Each card is a PageGrid.Col as="li"—no div wrapper.
## Features
- Header with heading and optional description
- Responsive grid: 3 cards/row at all breakpoints (base, md, lg)
- Aspect ratios: sm 3:2, md/lg 4:3
- Light/dark mode support
## Usage
```tsx
import { CardsIconGrid } from 'shared/patterns/CardsIconGrid';
<CardsIconGrid
heading="Explore Tools"
description="Choose a tool to get started"
cards={[
{
icon: "/icons/docs.svg",
iconAlt: "Documentation",
heading: "Documentation",
description: "Access everything you need.",
},
]}
/>
```
## Props
| Prop | Type | Description |
|------|------|-------------|
| `heading` | `string` | Section heading |
| `description` | `string` | Optional description |
| `cards` | `CardTextIconCardData[]` | Array of card configs |
| `className` | `string` | Additional CSS classes |
## Grid Column Spans
- base: 4, md: 4, lg: 4 (3 cards per row)
## Files
- `CardsIconGrid.tsx` - React component
- `CardsIconGrid.scss` - Styles
- `index.ts` - Barrel exports
- `README.md` - This file

View File

@@ -0,0 +1,4 @@
export {
CardsIconGrid,
type CardsIconGridProps,
} from './CardsIconGrid';

View File

@@ -0,0 +1,87 @@
// BDS CardsTextGrid Pattern Styles
// Brand Design System - Section with heading, description, and grid of CardTextIconCard
//
// Naming Convention: BEM with 'bds' namespace
// .bds-cards-text-grid - Base section container
// .bds-cards-text-grid__header - Header section (heading + description)
// .bds-cards-text-grid__list - Grid list (ul as PageGrid.Row)
// =============================================================================
// Design Tokens (uses BDS spacing from _spacing.scss)
// =============================================================================
$bds-cards-text-grid-padding-base: $bds-space-2xl;
$bds-cards-text-grid-padding-md: $bds-space-3xl;
$bds-cards-text-grid-padding-lg: $bds-space-4xl;
$bds-cards-text-grid-header-gap-base: $bds-gap-header-sm;
$bds-cards-text-grid-header-gap-md: $bds-gap-header-md;
$bds-cards-text-grid-header-gap-lg: $bds-gap-header-lg;
$bds-cards-text-grid-header-margin-base: $bds-gap-section-sm;
$bds-cards-text-grid-header-margin-md: $bds-gap-section-md;
$bds-cards-text-grid-header-margin-lg: $bds-gap-section-lg;
// =============================================================================
// Section Container
// =============================================================================
.bds-cards-text-grid {
padding-top: $bds-cards-text-grid-padding-base;
@include media-breakpoint-up(md) {
padding-top: $bds-cards-text-grid-padding-md;
}
@include media-breakpoint-up(lg) {
padding-top: $bds-cards-text-grid-padding-lg;
}
ul {
padding-left: 0;
list-style: none;
}
@include bds-theme-mode(dark) {
color: $white;
}
}
// =============================================================================
// Header Section
// =============================================================================
.bds-cards-text-grid__header {
display: flex;
flex-direction: column;
gap: $bds-cards-text-grid-header-gap-base;
margin-bottom: $bds-cards-text-grid-header-margin-base;
@include media-breakpoint-up(md) {
gap: $bds-cards-text-grid-header-gap-md;
margin-bottom: $bds-cards-text-grid-header-margin-md;
}
@include media-breakpoint-up(lg) {
gap: $bds-cards-text-grid-header-gap-lg;
margin-bottom: $bds-cards-text-grid-header-margin-lg;
}
h2, p {
margin-bottom: 0;
}
}
// =============================================================================
// List Grid - Aspect Ratios
// =============================================================================
.bds-cards-text-grid__list > li {
aspect-ratio: 16 / 9;
@include media-breakpoint-up(md) {
aspect-ratio: 3 / 2;
}
@include media-breakpoint-up(lg) {
aspect-ratio: 3 / 1;
}
}

View File

@@ -0,0 +1,62 @@
import React from 'react';
import clsx from 'clsx';
import { PageGrid, PageGridRow, PageGridCol } from 'shared/components/PageGrid/page-grid';
import { CardTextIconCard, CardTextIconCardProps } from 'shared/components/CardTextIcon';
export interface CardsTextGridProps {
/** Section heading (required) */
heading: string;
/** Optional description text */
description?: string;
/** Array of card data to display */
cards: CardTextIconCardProps[];
/** Additional CSS classes */
className?: string;
}
/**
* CardsTextGrid Component
*
* A section pattern with a header (heading + description) and a grid of
* CardTextIconCard components. Uses PageGrid.Row as="ul" with cards as
* PageGrid.Col as="li". Aspect ratios: sm 16:9, md 3:2, lg 3:1.
*
* @example
* <CardsTextGrid
* heading="Explore Tools"
* description="Choose a tool to get started"
* cards={[
* { icon: "/icons/docs.svg", heading: "Documentation", description: "..." },
* ]}
* />
*/
export const CardsTextGrid: React.FC<CardsTextGridProps> = ({
heading,
description,
cards,
className,
}) => {
return (
<PageGrid className={clsx('bds-cards-text-grid', className)}>
<PageGridRow>
<PageGridCol className="bds-cards-text-grid__header" span={{ base: 12, md: 6, lg: 8 }}>
<h2 className="h-md">{heading}</h2>
{description && <p className="body-l">{description}</p>}
</PageGridCol>
</PageGridRow>
<PageGridRow as="ul" className="bds-cards-text-grid__list list-none pl-0">
{cards.map((card, index) => (
<CardTextIconCard
key={card.heading || index}
heading={card.heading}
description={card.description}
gridColSpan={{ base: 4, md: 4, lg: 6 }}
/>
))}
</PageGridRow>
</PageGrid>
);
};
export default CardsTextGrid;

View File

@@ -0,0 +1,53 @@
# CardsTextGrid Pattern
A section pattern with a header (heading + description) and a grid of CardTextIconCard components.
## Overview
CardsTextGrid mirrors the LinkTextDirectory header structure and renders cards in a responsive grid using PageGrid.Row as="ul" and CardTextIconCard with gridColSpan. Each card is a PageGrid.Col as="li"—no div wrapper.
## Features
- Header with heading and optional description
- Responsive grid: 3 cards/row at base+md, 2 cards/row at lg
- Aspect ratios: sm 16:9, md 3:2, lg 3:1
- Light/dark mode support
## Usage
```tsx
import { CardsTextGrid } from 'shared/patterns/CardsTextGrid';
<CardsTextGrid
heading="Explore Tools"
description="Choose a tool to get started"
cards={[
{
icon: "/icons/docs.svg",
iconAlt: "Documentation",
heading: "Documentation",
description: "Access everything you need.",
},
]}
/>
```
## Props
| Prop | Type | Description |
|------|------|-------------|
| `heading` | `string` | Section heading |
| `description` | `string` | Optional description |
| `cards` | `CardTextIconCardData[]` | Array of card configs |
| `className` | `string` | Additional CSS classes |
## Grid Column Spans
- base: 4, md: 4, lg: 6 (3 cards/row at base+md, 2 at lg)
## Files
- `CardsTextGrid.tsx` - React component
- `CardsTextGrid.scss` - Styles
- `index.ts` - Barrel exports
- `README.md` - This file

View File

@@ -0,0 +1,4 @@
export {
CardsTextGrid,
type CardsTextGridProps,
} from './CardsTextGrid';