# LinkTextDirectory Component
A section pattern that displays a numbered list of LinkTextCard components with a heading and optional description.
## Overview
LinkTextDirectory is a section-level pattern that combines a header (heading + description) with a vertically stacked list of LinkTextCard components. Each card is automatically numbered sequentially (01, 02, 03...), making it perfect for feature lists, step-by-step guides, or numbered content sections.
## Features
- **Automatic Numbering**: Cards are numbered sequentially starting from 01
- **Responsive Layout**: Adaptive spacing and alignment across breakpoints
- **Desktop Right-Alignment**: Cards are right-aligned on desktop for visual interest
- **Minimal HTML Structure**: Flat, efficient DOM hierarchy
- **Light/Dark Mode**: Full theming support
- **Flexible Content**: Supports any number of cards
## Layout Behavior
| Breakpoint | Card Alignment | Gap Between Cards | Header Gap |
|------------|----------------|-------------------|------------|
| Base (< 576px) | Left | 24px | 8px |
| MD (576px - 991px) | Left | 32px | 8px |
| LG (≥ 992px) | Right | 40px | 16px |
**Desktop Behavior**: Cards are right-aligned using `align-items: flex-end`, creating a visually distinct layout compared to mobile/tablet.
## Usage
### Basic Usage
```tsx
```
### Without Description
```tsx
```
### With Dynamic Data
```tsx
const features = [
{
heading: "Feature One",
description: "Description for feature one",
buttons: [{ label: "Learn More", href: "/feature-1" }]
},
// ... more features
];
```
## Props
### LinkTextDirectoryProps
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `heading` | `string` | Required | Section heading |
| `description` | `string` | - | Optional description text |
| `cards` | `LinkTextCardData[]` | Required | Array of card data |
| `className` | `string` | - | Additional CSS classes |
### LinkTextCardData
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `heading` | `string` | Required | Card heading |
| `description` | `string` | Required | Card description |
| `buttons` | `ButtonConfig[]` | Required | Array of button configs (max 2) |
### ButtonConfig (from ButtonGroup)
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string` | Required | Button text |
| `href` | `string` | - | Link destination |
| `onClick` | `() => void` | - | Click handler |
## Component Structure
```tsx
{heading}
{description}
{cards.map((card, index) => (
))}
```
**Key Design Decisions:**
- **PageGrid Integration**: Uses PageGrid system for responsive layout
- **Typography Classes**: Uses existing `h-md` and `body-l` utility classes
- **Flexbox Header**: Header uses flexbox with gap for spacing between heading and description
- **Desktop Right-Alignment**: Cards offset by 4 columns at LG breakpoint (right-aligned)
- **Semantic List**: Cards wrapped in `
` with each card as `- `
## Responsive Spacing
| Breakpoint | Section Padding | Header Gap | Header Margin-Bottom |
|------------|-----------------|------------|----------------------|
| Base (< 576px) | 24px | 8px | 24px |
| MD (576px - 991px) | 32px | 8px | 32px |
| LG (≥ 992px) | 40px | 16px | 40px |
**Section Padding**: Top and bottom padding on the entire section
**Header Gap**: Space between heading and description (via flexbox gap)
**Header Margin-Bottom**: Space between header and cards list
## Styling
### CSS Classes
- `.bds-link-text-directory` - Main PageGrid container with section padding
- `.bds-link-text-directory__header` - Header section (flexbox column with gap)
### Typography
- **Heading**: `h-md` class (responsive heading)
- **Description**: `body-l` class (large body text)
- Card content uses LinkTextCard's built-in typography
### Grid Layout
- **Header Column**: `span={{ base: 12, md: 6, lg: 8 }}`
- **Cards Column**: `span={{ base: 12, md: 8, lg: 8 }}` with `offset={{ lg: 4 }}`
- Cards are right-aligned on desktop via the 4-column offset
### Dark Mode
- Text color changes to white in dark mode
- Applied to entire section via `bds-theme-mode(dark)` mixin
## Card Numbering
Cards are automatically numbered based on their array index:
```typescript
cards[0] → LinkTextCard(index: 0) → displays "01"
cards[1] → LinkTextCard(index: 1) → displays "02"
cards[2] → LinkTextCard(index: 2) → displays "03"
// ... and so on
```
## Files
- `LinkTextDirectory.tsx` - React component
- `LinkTextDirectory.scss` - SCSS styles
- `index.ts` - Barrel exports
- `README.md` - This file
## Related Components
- **LinkTextCard**: Used for each card in the list
- **ButtonGroup**: Used by LinkTextCard for action buttons
## Import
```tsx
import { LinkTextDirectory } from 'shared/sections/LinkTextDirectory';
// or
import {
LinkTextDirectory,
type LinkTextDirectoryProps,
type LinkTextCardData
} from 'shared/sections/LinkTextDirectory';
```
## Design System
Part of the Brand Design System (BDS) with `bds-` namespace prefix.
## Best Practices
1. **Consistent Card Content**: Try to keep similar text lengths across cards for visual balance
2. **Limit Cards**: 3-6 cards works best for readability
3. **Clear Descriptions**: Keep descriptions concise but informative
4. **Button Labels**: Use clear, action-oriented button labels
5. **Logical Ordering**: Order cards by priority or logical sequence
## Accessibility
- Semantic HTML with proper heading hierarchy (`
` for section, `` for cards)
- Semantic list structure: `` containing `- ` elements
- Sequential tab order through cards and buttons
- ARIA-compliant button and link elements (via ButtonGroup)
- Maintains focus order: heading → description → card 1 → card 2 → etc.
## Best Practices for React Keys
When mapping over cards, use a stable identifier instead of array index:
```tsx
// ❌ Avoid using index as key
{cards.map((card, index) => (
))}
// ✅ Better: Use a unique identifier
{cards.map((card, index) => (
))}
// ✅ Best: Add an id field to LinkTextCardData
interface LinkTextCardData {
id: string; // Unique identifier
heading: string;
description: string;
buttons: ButtonConfig[];
}
{cards.map((card) => (
))}
```
## Example with All Features
```tsx
openDemo() }
]
},
{
heading: "Cost Effective",
description: "Minimal transaction fees make XRPL perfect for micro-transactions and high-volume use cases",
buttons: [
{ label: "See Pricing", href: "/pricing" }
]
},
{
heading: "Battle Tested",
description: "Over 10 years of continuous operation with billions of transactions processed",
buttons: [
{ label: "Read Case Studies", href: "/case-studies" },
{ label: "View Stats", href: "/statistics" }
]
}
]}
className="my-custom-class"
/>
```