# 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" /> ```