mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2026-01-29 02:55:22 +00:00
Compare commits
1 Commits
section/ca
...
section/ic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1aa2092bb0 |
257
about/cards-icon-grid-showcase.page.tsx
Normal file
257
about/cards-icon-grid-showcase.page.tsx
Normal file
@@ -0,0 +1,257 @@
|
||||
import { PageGrid, PageGridRow, PageGridCol } from "shared/components/PageGrid/page-grid";
|
||||
import { CardsIconGrid } from "shared/patterns/CardsIconGrid";
|
||||
import { Divider } from "shared/components/Divider";
|
||||
|
||||
export const frontmatter = {
|
||||
seo: {
|
||||
title: 'CardsIconGrid Pattern Showcase',
|
||||
description: "A comprehensive showcase of the CardsIconGrid pattern component demonstrating light and dark mode variations in the XRPL.org Design System.",
|
||||
}
|
||||
};
|
||||
|
||||
// Sample icon SVG for demonstration
|
||||
const SAMPLE_ICON = "/img/icons/card-icon-placeholder.svg";
|
||||
|
||||
// Sample cards data - Green variant
|
||||
const greenCards = [
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "Digital Wallets icon",
|
||||
label: "Digital Wallets",
|
||||
href: "#wallets",
|
||||
variant: "green" as const,
|
||||
},
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "B2B Payment Rails icon",
|
||||
label: "B2B Payment Rails",
|
||||
href: "#payments",
|
||||
variant: "green" as const,
|
||||
},
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "Compliance-First Payments icon",
|
||||
label: "Compliance-First Payments",
|
||||
href: "#compliance",
|
||||
variant: "green" as const,
|
||||
},
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "Merchant Settlement icon",
|
||||
label: "Merchant Settlement",
|
||||
href: "#settlement",
|
||||
variant: "green" as const,
|
||||
},
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "Cross-Border Payments icon",
|
||||
label: "Cross-Border Payments",
|
||||
href: "#cross-border",
|
||||
variant: "green" as const,
|
||||
},
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "Treasury Management icon",
|
||||
label: "Treasury Management",
|
||||
href: "#treasury",
|
||||
variant: "green" as const,
|
||||
},
|
||||
];
|
||||
|
||||
// Sample cards data - Neutral variant
|
||||
const neutralCards = [
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "Documentation icon",
|
||||
label: "Documentation",
|
||||
href: "#docs",
|
||||
variant: "neutral" as const,
|
||||
},
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "Tutorials icon",
|
||||
label: "Tutorials",
|
||||
href: "#tutorials",
|
||||
variant: "neutral" as const,
|
||||
},
|
||||
{
|
||||
icon: SAMPLE_ICON,
|
||||
iconAlt: "API Reference icon",
|
||||
label: "API Reference",
|
||||
href: "#api",
|
||||
variant: "neutral" as const,
|
||||
},
|
||||
];
|
||||
|
||||
export default function CardsIconGridShowcase() {
|
||||
return (
|
||||
<div className="landing">
|
||||
<div className="overflow-hidden">
|
||||
{/* Hero Section */}
|
||||
<section className="py-26 text-center">
|
||||
<div className="col-lg-8 mx-auto">
|
||||
<h6 className="eyebrow mb-3">Pattern Showcase</h6>
|
||||
<h1 className="mb-4">CardsIconGrid Pattern</h1>
|
||||
<p className="longform">
|
||||
A section pattern that displays a heading, optional description, and a responsive grid
|
||||
of CardIcon components. Follows the "CardIconGrid" design from Figma.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Design Tokens Reference */}
|
||||
<PageGrid className="py-10">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={{ base: 4, md: 8, lg: 12 }}>
|
||||
<h2 className="h4 mb-6">Design Specifications</h2>
|
||||
<div className="d-flex flex-wrap gap-8">
|
||||
<div style={{ flex: '1 1 250px' }}>
|
||||
<h6 className="mb-3">Typography</h6>
|
||||
<ul className="mb-0">
|
||||
<li><strong>Heading:</strong> heading-md (Tobias Light)</li>
|
||||
<li><strong>Description:</strong> body-l (Booton Light)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style={{ flex: '1 1 250px' }}>
|
||||
<h6 className="mb-3">Grid Layout</h6>
|
||||
<ul className="mb-0">
|
||||
<li><strong>Mobile:</strong> 1 column</li>
|
||||
<li><strong>Tablet:</strong> 2 columns</li>
|
||||
<li><strong>Desktop:</strong> 3 columns</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style={{ flex: '1 1 250px' }}>
|
||||
<h6 className="mb-3">Colors</h6>
|
||||
<ul className="mb-0">
|
||||
<li><strong>Light Mode:</strong> $black (#141414)</li>
|
||||
<li><strong>Dark Mode:</strong> $white (#FFFFFF)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* 6 Cards Example - Green Variant */}
|
||||
<section>
|
||||
<CardsIconGrid
|
||||
heading="Unlock new business models with embedded payments"
|
||||
description="Streamline development and build powerful RWA tokenization solutions with XRP Ledger's comprehensive developer toolset."
|
||||
cards={greenCards}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* 3 Cards Example - Neutral Variant */}
|
||||
<section>
|
||||
<CardsIconGrid
|
||||
heading="Developer Resources"
|
||||
description="Everything you need to start building on the XRP Ledger."
|
||||
cards={neutralCards}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Without Description */}
|
||||
<PageGrid className="py-10">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={{ base: 4, md: 8, lg: 12 }}>
|
||||
<h2 className="h4 mb-4">Without Description</h2>
|
||||
<p className="mb-0">
|
||||
The description prop is optional. When omitted, only the heading appears above the cards.
|
||||
</p>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
<section>
|
||||
<CardsIconGrid
|
||||
heading="Funding & Support Programs"
|
||||
cards={greenCards.slice(0, 3)}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Code Examples */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={{ base: 4, md: 8, lg: 10 }}>
|
||||
<h2 className="h4 mb-6">Code Examples</h2>
|
||||
|
||||
<h5 className="mb-4">Basic Usage</h5>
|
||||
<div className="p-4 mb-8 br-4" style={{ backgroundColor: '#1a1a1a', fontFamily: 'monospace', fontSize: '14px' }}>
|
||||
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', color: '#f8f8f2' }}>{`import { CardsIconGrid } from 'shared/patterns/CardsIconGrid';
|
||||
|
||||
<CardsIconGrid
|
||||
heading="Unlock new business models"
|
||||
description="Build powerful solutions with XRPL."
|
||||
cards={[
|
||||
{
|
||||
icon: "/icons/wallet.svg",
|
||||
label: "Digital Wallets",
|
||||
href: "/docs/wallets",
|
||||
variant: "green"
|
||||
},
|
||||
{
|
||||
icon: "/icons/payments.svg",
|
||||
label: "B2B Payment Rails",
|
||||
href: "/docs/payments",
|
||||
variant: "green"
|
||||
},
|
||||
{
|
||||
icon: "/icons/compliance.svg",
|
||||
label: "Compliance-First Payments",
|
||||
href: "/docs/compliance",
|
||||
variant: "green"
|
||||
}
|
||||
]}
|
||||
/>`}</pre>
|
||||
</div>
|
||||
|
||||
<h5 className="mb-4">Without Description</h5>
|
||||
<div className="p-4 mb-8 br-4" style={{ backgroundColor: '#1a1a1a', fontFamily: 'monospace', fontSize: '14px' }}>
|
||||
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', color: '#f8f8f2' }}>{`<CardsIconGrid
|
||||
heading="Developer Resources"
|
||||
cards={[
|
||||
{ icon: "/icons/docs.svg", label: "Documentation", href: "/docs", variant: "neutral" },
|
||||
{ icon: "/icons/tutorials.svg", label: "Tutorials", href: "/tutorials", variant: "neutral" },
|
||||
{ icon: "/icons/api.svg", label: "API Reference", href: "/api", variant: "neutral" }
|
||||
]}
|
||||
/>`}</pre>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Design References */}
|
||||
<PageGrid className="py-26">
|
||||
<PageGridRow>
|
||||
<PageGridCol span={{ base: 4, md: 8, lg: 12 }}>
|
||||
<h2 className="h4 mb-6">Design References</h2>
|
||||
<div className="d-flex flex-column gap-3">
|
||||
<div>
|
||||
<strong>Figma:</strong>{' '}
|
||||
<a href="https://www.figma.com/design/Ojj6UpFBw3HMb0QqRaKxAU/Section-Cards---Icon?node-id=30071-3082&m=dev" target="_blank" rel="noopener noreferrer">
|
||||
Section Cards - Icon Grid
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Documentation:</strong>{' '}
|
||||
<code>shared/patterns/CardsIconGrid/CardsIconGrid.md</code>
|
||||
</div>
|
||||
</div>
|
||||
</PageGridCol>
|
||||
</PageGridRow>
|
||||
</PageGrid>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
141
shared/patterns/CardsIconGrid/CardsIconGrid.md
Normal file
141
shared/patterns/CardsIconGrid/CardsIconGrid.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# CardsIconGrid Pattern
|
||||
|
||||
A section pattern that displays a heading, optional description, and a responsive grid of `CardIcon` components. Follows the "CardIconGrid" design from Figma.
|
||||
|
||||
## Features
|
||||
|
||||
- Responsive grid layout (1 column mobile, 2 tablet, 3 desktop)
|
||||
- Heading with `heading-md` typography (Tobias Light)
|
||||
- Optional description with `body-l` typography (Booton Light)
|
||||
- Proper spacing using `PageGrid` for container and alignment
|
||||
- Full dark mode support
|
||||
- Uses the existing `CardIcon` component for cards
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { CardsIconGrid } from 'shared/patterns/CardsIconGrid';
|
||||
|
||||
<CardsIconGrid
|
||||
heading="Unlock new business models with embedded payments"
|
||||
description="Streamline development and build powerful RWA tokenization solutions with XRP Ledger's comprehensive developer toolset."
|
||||
cards={[
|
||||
{
|
||||
icon: "/icons/wallet.svg",
|
||||
label: "Digital Wallets",
|
||||
href: "/docs/wallets",
|
||||
variant: "green"
|
||||
},
|
||||
{
|
||||
icon: "/icons/payments.svg",
|
||||
label: "B2B Payment Rails",
|
||||
href: "/docs/payments",
|
||||
variant: "green"
|
||||
},
|
||||
{
|
||||
icon: "/icons/compliance.svg",
|
||||
label: "Compliance-First Payments",
|
||||
href: "/docs/compliance",
|
||||
variant: "green"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| `heading` | `React.ReactNode` | Yes | Section heading text |
|
||||
| `description` | `React.ReactNode` | No | Section description text (optional) |
|
||||
| `cards` | `CardsIconGridCardConfig[]` | Yes | Array of card configurations (uses `CardIconProps`) |
|
||||
| `className` | `string` | No | Additional CSS class names |
|
||||
|
||||
### CardsIconGridCardConfig
|
||||
|
||||
Each card in the `cards` array accepts all props from `CardIconProps`:
|
||||
|
||||
| Prop | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| `icon` | `string` | Yes | Icon image source (URL or path) |
|
||||
| `iconAlt` | `string` | No | Alt text for the icon image |
|
||||
| `label` | `string` | Yes | Card label text |
|
||||
| `variant` | `'neutral' \| 'green'` | No | Color variant (default: 'neutral') |
|
||||
| `href` | `string` | No | Link destination - renders as `<a>` |
|
||||
| `onClick` | `() => void` | No | Click handler - renders as `<button>` |
|
||||
| `disabled` | `boolean` | No | Disabled state |
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
| Breakpoint | Grid Columns | Vertical Padding |
|
||||
|------------|--------------|------------------|
|
||||
| Mobile (< 768px) | 1 column | 48px |
|
||||
| Tablet (768px - 1199px) | 2 columns | 64px |
|
||||
| Desktop (≥ 1200px) | 3 columns | 80px |
|
||||
|
||||
## Design Tokens
|
||||
|
||||
### Colors
|
||||
|
||||
| Mode | Element | Color |
|
||||
|------|---------|-------|
|
||||
| Light | Heading | `$black` (#141414) |
|
||||
| Light | Description | `$black` (#141414) |
|
||||
| Dark | Heading | `$white` (#FFFFFF) |
|
||||
| Dark | Description | `$white` (#FFFFFF) |
|
||||
|
||||
### Spacing
|
||||
|
||||
- Header gap (heading to description): 8px mobile/tablet, 16px desktop
|
||||
- Section gap (header to cards): 24px mobile, 32px tablet, 40px desktop
|
||||
- Cards column gap: 24px mobile, 8px tablet/desktop
|
||||
- Cards row gap: 24px mobile, 32px tablet, 40px desktop
|
||||
|
||||
## CSS Classes
|
||||
|
||||
| Class | Description |
|
||||
|-------|-------------|
|
||||
| `.bds-cards-icon-grid` | Base section container |
|
||||
| `.bds-cards-icon-grid__header` | Header wrapper for heading and description |
|
||||
| `.bds-cards-icon-grid__heading` | Section heading (uses `.h-md`) |
|
||||
| `.bds-cards-icon-grid__description` | Section description (uses `.body-l`) |
|
||||
| `.bds-cards-icon-grid__cards` | Cards grid container |
|
||||
| `.bds-cards-icon-grid__card-wrapper` | Individual card wrapper |
|
||||
|
||||
## Card Variants
|
||||
|
||||
The pattern supports both CardIcon variants:
|
||||
|
||||
### Green Variant
|
||||
```tsx
|
||||
cards={[
|
||||
{ icon: "/icons/wallet.svg", label: "Digital Wallets", href: "/wallets", variant: "green" }
|
||||
]}
|
||||
```
|
||||
|
||||
### Neutral Variant
|
||||
```tsx
|
||||
cards={[
|
||||
{ icon: "/icons/docs.svg", label: "Documentation", href: "/docs", variant: "neutral" }
|
||||
]}
|
||||
```
|
||||
|
||||
## Without Description
|
||||
|
||||
The description prop is optional:
|
||||
|
||||
```tsx
|
||||
<CardsIconGrid
|
||||
heading="Funding & Support Programs"
|
||||
cards={[...]}
|
||||
/>
|
||||
```
|
||||
|
||||
## Figma Reference
|
||||
|
||||
- Design: [Section Cards - Icon Grid](https://www.figma.com/design/Ojj6UpFBw3HMb0QqRaKxAU/Section-Cards---Icon?node-id=30071-3082&m=dev)
|
||||
|
||||
## Showcase
|
||||
|
||||
View the pattern showcase at: `/about/cards-icon-grid-showcase`
|
||||
|
||||
180
shared/patterns/CardsIconGrid/CardsIconGrid.scss
Normal file
180
shared/patterns/CardsIconGrid/CardsIconGrid.scss
Normal file
@@ -0,0 +1,180 @@
|
||||
// BDS CardsIconGrid Pattern Styles
|
||||
// Brand Design System - Section with heading, optional description, and grid of CardIcon components
|
||||
//
|
||||
// Naming Convention: BEM with 'bds' namespace
|
||||
// .bds-cards-icon-grid - Base section container
|
||||
// .bds-cards-icon-grid__header - Header wrapper for heading and description
|
||||
// .bds-cards-icon-grid__heading - Section heading (uses .h-md)
|
||||
// .bds-cards-icon-grid__description - Section description (uses .body-l)
|
||||
// .bds-cards-icon-grid__cards - Cards grid container
|
||||
// .bds-cards-icon-grid__card-wrapper - Individual card wrapper
|
||||
//
|
||||
// Design tokens from Figma:
|
||||
// Light Mode:
|
||||
// - Heading: Neutral Black (#141414) → $black
|
||||
// - Description: Neutral Black (#141414) → $black
|
||||
//
|
||||
// Dark Mode:
|
||||
// - Heading: Neutral White (#FFFFFF) → $white
|
||||
// - Description: Neutral White (#FFFFFF) → $white
|
||||
//
|
||||
// - Header content max-width: 808px (approximately 8 columns at desktop)
|
||||
// - Gap between heading and description: 8px mobile/tablet, 16px desktop
|
||||
// - Gap between cards: 8px (matches $bds-grid-gutter)
|
||||
|
||||
// =============================================================================
|
||||
// Design Tokens (from Figma)
|
||||
// =============================================================================
|
||||
|
||||
$bds-grid-gutter: 8px;
|
||||
|
||||
// Spacing - Header gap (between heading and description)
|
||||
$bds-cards-icon-grid-header-gap-sm: 8px; // Mobile: 8px
|
||||
$bds-cards-icon-grid-header-gap-md: 8px; // Tablet: 8px
|
||||
$bds-cards-icon-grid-header-gap-lg: 16px; // Desktop: 16px
|
||||
|
||||
// Spacing - Section gap (between header and cards)
|
||||
$bds-cards-icon-grid-section-gap-sm: 24px; // Mobile
|
||||
$bds-cards-icon-grid-section-gap-md: 32px; // Tablet
|
||||
$bds-cards-icon-grid-section-gap-lg: 40px; // Desktop
|
||||
|
||||
// Spacing - Cards gap
|
||||
$bds-cards-icon-grid-cards-gap-sm: 24px; // Mobile: 24px vertical stack
|
||||
$bds-cards-icon-grid-cards-gap-md: 8px; // Tablet: 8px
|
||||
$bds-cards-icon-grid-cards-gap-lg: 8px; // Desktop: 8px
|
||||
|
||||
// Spacing - Row gap (between rows of cards)
|
||||
$bds-cards-icon-grid-row-gap-sm: 24px; // Mobile
|
||||
$bds-cards-icon-grid-row-gap-md: 32px; // Tablet
|
||||
$bds-cards-icon-grid-row-gap-lg: 40px; // Desktop
|
||||
|
||||
// Spacing - Section padding (vertical)
|
||||
$bds-cards-icon-grid-padding-y-sm: 48px; // Mobile
|
||||
$bds-cards-icon-grid-padding-y-md: 64px; // Tablet
|
||||
$bds-cards-icon-grid-padding-y-lg: 80px; // Desktop
|
||||
|
||||
// Colors - Light Mode (default)
|
||||
$bds-cards-icon-grid-heading-color: $black; // #141414 - Neutral black
|
||||
$bds-cards-icon-grid-description-color: $black; // #141414 - Neutral black
|
||||
|
||||
// Colors - Dark Mode
|
||||
$bds-cards-icon-grid-heading-color-dark: $white; // #FFFFFF - Neutral white
|
||||
$bds-cards-icon-grid-description-color-dark: $white; // #FFFFFF - Neutral white
|
||||
|
||||
// =============================================================================
|
||||
// Section Container
|
||||
// =============================================================================
|
||||
|
||||
.bds-cards-icon-grid {
|
||||
width: 100%;
|
||||
padding-top: $bds-cards-icon-grid-padding-y-sm;
|
||||
padding-bottom: $bds-cards-icon-grid-padding-y-sm;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
padding-top: $bds-cards-icon-grid-padding-y-md;
|
||||
padding-bottom: $bds-cards-icon-grid-padding-y-md;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
padding-top: $bds-cards-icon-grid-padding-y-lg;
|
||||
padding-bottom: $bds-cards-icon-grid-padding-y-lg;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Header Section
|
||||
// =============================================================================
|
||||
|
||||
.bds-cards-icon-grid__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $bds-cards-icon-grid-header-gap-sm;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
gap: $bds-cards-icon-grid-header-gap-md;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
gap: $bds-cards-icon-grid-header-gap-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__heading {
|
||||
margin: 0;
|
||||
// Typography handled by .h-md class from _font.scss
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__description {
|
||||
margin: 0;
|
||||
// Typography handled by .body-l class from _font.scss
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Cards Grid
|
||||
// =============================================================================
|
||||
|
||||
.bds-cards-icon-grid__cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: $bds-cards-icon-grid-cards-gap-sm;
|
||||
width: 100%;
|
||||
margin-top: $bds-cards-icon-grid-section-gap-sm;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
column-gap: $bds-cards-icon-grid-cards-gap-md;
|
||||
row-gap: $bds-cards-icon-grid-row-gap-md;
|
||||
margin-top: $bds-cards-icon-grid-section-gap-md;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
column-gap: $bds-cards-icon-grid-cards-gap-lg;
|
||||
row-gap: $bds-cards-icon-grid-row-gap-lg;
|
||||
margin-top: $bds-cards-icon-grid-section-gap-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__card-wrapper {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
|
||||
// Ensure CardIcon fills the wrapper
|
||||
.bds-card-icon {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Light Mode Styles
|
||||
// =============================================================================
|
||||
|
||||
html.light {
|
||||
.bds-cards-icon-grid {
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__heading {
|
||||
color: $bds-cards-icon-grid-heading-color;
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__description {
|
||||
color: $bds-cards-icon-grid-description-color;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Dark Mode Styles
|
||||
// =============================================================================
|
||||
|
||||
html.dark {
|
||||
.bds-cards-icon-grid__heading {
|
||||
color: $bds-cards-icon-grid-heading-color-dark;
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__description {
|
||||
color: $bds-cards-icon-grid-description-color-dark;
|
||||
}
|
||||
}
|
||||
|
||||
121
shared/patterns/CardsIconGrid/CardsIconGrid.tsx
Normal file
121
shared/patterns/CardsIconGrid/CardsIconGrid.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { CardIcon, CardIconProps } from '../../components/CardIcon';
|
||||
import { PageGrid } from '../../components/PageGrid/page-grid';
|
||||
|
||||
/**
|
||||
* Configuration for a single card in the CardsIconGrid pattern
|
||||
*/
|
||||
export type CardsIconGridCardConfig = CardIconProps;
|
||||
|
||||
/**
|
||||
* Props for the CardsIconGrid pattern component
|
||||
*/
|
||||
export interface CardsIconGridProps extends React.ComponentPropsWithoutRef<'section'> {
|
||||
/** Section heading text */
|
||||
heading: React.ReactNode;
|
||||
/** Section description text (optional) */
|
||||
description?: React.ReactNode;
|
||||
/** Array of card configurations (uses CardIconProps) */
|
||||
cards: readonly CardsIconGridCardConfig[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique key for a card based on its props
|
||||
*/
|
||||
const getCardKey = (card: CardsIconGridCardConfig, index: number): string => {
|
||||
if (card.href) return `card-${card.href}-${index}`;
|
||||
if (card.label) return `card-${card.label.toString().slice(0, 20)}-${index}`;
|
||||
return `card-${index}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* CardsIconGrid Pattern Component
|
||||
*
|
||||
* A section pattern that displays a heading, optional description, and a responsive grid
|
||||
* of CardIcon components. Follows the "CardIconGrid" pattern from Figma.
|
||||
*
|
||||
* Features:
|
||||
* - Responsive grid layout (1 column mobile, 2 tablet, 3 desktop)
|
||||
* - Heading with `heading-md` typography (Tobias Light)
|
||||
* - Optional description with `body-l` typography (Booton Light)
|
||||
* - Proper spacing using PageGrid for container and alignment
|
||||
* - Full dark mode support
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <CardsIconGrid
|
||||
* heading="Unlock new business models with embedded payments"
|
||||
* description="Streamline development and build powerful solutions."
|
||||
* cards={[
|
||||
* {
|
||||
* icon: "/icons/wallet.svg",
|
||||
* label: "Digital Wallets",
|
||||
* href: "/docs/wallets",
|
||||
* variant: "green"
|
||||
* },
|
||||
* {
|
||||
* icon: "/icons/payments.svg",
|
||||
* label: "B2B Payment Rails",
|
||||
* href: "/docs/payments",
|
||||
* variant: "green"
|
||||
* }
|
||||
* ]}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export const CardsIconGrid = React.forwardRef<HTMLElement, CardsIconGridProps>(
|
||||
function CardsIconGrid(
|
||||
{ className, heading, description, cards, ...rest },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<section
|
||||
ref={ref}
|
||||
className={clsx('bds-cards-icon-grid', className)}
|
||||
{...rest}
|
||||
>
|
||||
<PageGrid>
|
||||
{/* Header content row */}
|
||||
<PageGrid.Row>
|
||||
<PageGrid.Col
|
||||
span={{
|
||||
base: 'fill',
|
||||
md: 6,
|
||||
lg: 8,
|
||||
}}
|
||||
>
|
||||
<div className="bds-cards-icon-grid__header">
|
||||
<h2 className="bds-cards-icon-grid__heading h-md">{heading}</h2>
|
||||
{description && (
|
||||
<p className="bds-cards-icon-grid__description body-l">{description}</p>
|
||||
)}
|
||||
</div>
|
||||
</PageGrid.Col>
|
||||
</PageGrid.Row>
|
||||
|
||||
{/* Cards grid row */}
|
||||
<PageGrid.Row>
|
||||
<PageGrid.Col span="fill">
|
||||
<div className="bds-cards-icon-grid__cards">
|
||||
{cards.map((card, index) => (
|
||||
<div
|
||||
key={getCardKey(card, index)}
|
||||
className="bds-cards-icon-grid__card-wrapper"
|
||||
>
|
||||
<CardIcon {...card} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</PageGrid.Col>
|
||||
</PageGrid.Row>
|
||||
</PageGrid>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
CardsIconGrid.displayName = 'CardsIconGrid';
|
||||
|
||||
export default CardsIconGrid;
|
||||
|
||||
3
shared/patterns/CardsIconGrid/index.ts
Normal file
3
shared/patterns/CardsIconGrid/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { CardsIconGrid, type CardsIconGridProps, type CardsIconGridCardConfig } from './CardsIconGrid';
|
||||
export { default } from './CardsIconGrid';
|
||||
|
||||
@@ -19938,6 +19938,98 @@ html.light .bds-callout-media-banner--image-text-black .bds-callout-media-banner
|
||||
color: #141414 !important;
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid {
|
||||
width: 100%;
|
||||
padding-top: 48px;
|
||||
padding-bottom: 48px;
|
||||
}
|
||||
@media (min-width: 576px) {
|
||||
.bds-cards-icon-grid {
|
||||
padding-top: 64px;
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.bds-cards-icon-grid {
|
||||
padding-top: 80px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
@media (min-width: 576px) {
|
||||
.bds-cards-icon-grid__header {
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.bds-cards-icon-grid__header {
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__heading {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__description {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
}
|
||||
@media (min-width: 576px) {
|
||||
.bds-cards-icon-grid__cards {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
column-gap: 8px;
|
||||
row-gap: 32px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.bds-cards-icon-grid__cards {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
column-gap: 8px;
|
||||
row-gap: 40px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.bds-cards-icon-grid__card-wrapper {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.bds-cards-icon-grid__card-wrapper .bds-card-icon {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
html.light .bds-cards-icon-grid {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
html.light .bds-cards-icon-grid__heading {
|
||||
color: #141414;
|
||||
}
|
||||
html.light .bds-cards-icon-grid__description {
|
||||
color: #141414;
|
||||
}
|
||||
|
||||
html.dark .bds-cards-icon-grid__heading {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
html.dark .bds-cards-icon-grid__description {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
pre {
|
||||
color: #FFFFFF;
|
||||
background-color: #232325;
|
||||
|
||||
@@ -101,6 +101,7 @@ $line-height-base: 1.5;
|
||||
@import "../shared/patterns/HeaderHeroSplitMedia/HeaderHeroSplitMedia.scss";
|
||||
@import "../shared/patterns/CardsFeatured/CardsFeatured.scss";
|
||||
@import "../shared/patterns/CalloutMediaBanner/CalloutMediaBanner.scss";
|
||||
@import "../shared/patterns/CardsIconGrid/CardsIconGrid.scss";
|
||||
@import "_code-tabs.scss";
|
||||
@import "_code-walkthrough.scss";
|
||||
@import "_diagrams.scss";
|
||||
|
||||
Reference in New Issue
Block a user