# CardImage Component Documentation

## Overview

The CardImage component is a responsive card implementation following the XRPL Brand Design System (BDS). It displays an image, title, subtitle, and call-to-action button with three responsive size variants that adapt to viewport width.

## Features

- **Three Responsive Variants**: LG (≥992px), MD (576px-991px), SM (<576px)
- **Interactive States**: Default, Hover, Focus, Pressed, Disabled
- **Button Animation on Card Hover**: Hovering the card triggers the button's hover animation
- **Flexible Usage**: Supports both link navigation and click handlers
- **Accessibility**: Keyboard navigation and screen reader support


## Props API

```typescript
interface CardImageProps {
  /** Image source URL */
  image: string;
  /** Alt text for the image */
  imageAlt: string;
  /** Card title (1 line only) */
  title: string;
  /** Card subtitle (max 3 lines). `\n` renders as a line break; an all-bullet subtitle renders as a list. */
  subtitle: string;
  /** Button label text */
  buttonLabel: string;
  /** Link destination (renders card as clickable link) */
  href?: string;
  /** Click handler for the button */
  onClick?: () => void;
  /** Disabled state */
  disabled?: boolean;
  /** Optional className for custom styling */
  className?: string;
  /** When true, image fills entire container with object-fit: cover (no visible background) */
  fullBleed?: boolean;
  /** Custom background color for image container (defaults to gray-100) */
  backgroundColor?: string;
}
```

### Default Values

- `disabled`: `false`
- `className`: `''`
- `fullBleed`: `false`
- `backgroundColor`: `undefined` (falls back to the gray-100 image background)


## Subtitle Formatting

`subtitle` is a plain string, but it is not rendered blindly:

- Lines are split on `\n`, and blank lines are dropped.
- If **every** line begins with a bullet marker (`•`, `-`, or `*`), the subtitle
renders as a `<ul>` (`bds-card-image__subtitle--list`) with one `<li>` per line.
- Anything else renders as a single `<p>`, where `white-space: pre-line` means
`\n` still produces a visible line break.


```tsx
// Renders as a bulleted list
<CardImage subtitle={"• Fast settlement\n• Low fees\n• Built-in DEX"} ... />

// Renders as a paragraph with a line break
<CardImage subtitle={"Fast settlement.\nLow fees."} ... />
```

## Image Sizing

By default the image sits in a fixed-height container against a gray-100
background, preserving its aspect ratio.

- **`fullBleed`** — sets `bds-card-image--full-bleed`, which drops the card's
fixed height (`height: auto`) and makes the image fill the container using a
1:1 aspect ratio with `object-fit: cover`. Use this when the image is the whole
visual and you don't want letterboxing.
- **`backgroundColor`** — sets the `--bds-card-image-bg` custom property on the
image container, overriding the gray-100 default. Has no visible effect
alongside `fullBleed`, since the image covers the container.


```tsx
<CardImage fullBleed image="/images/hero.png" ... />
<CardImage backgroundColor="#F3F1EB" image="/images/logo.svg" ... />
```

## Responsive Variants

### LG (Large) - Desktop (≥992px)

- Card height: 620px
- Image height: 400px (1:1 aspect ratio preserved)
- 3-column grid width


### MD (Medium) - Tablet (576px - 991px)

- Card height: 560px
- Image height: 280px
- 2-column grid width


### SM (Small) - Mobile (<576px)

- Card height: 536px
- Image height: 268px
- 1-column grid width (full width)


## States

### Default State

The default interactive state with the button showing its primary green background.

### Hover State

When the user hovers over the card:

- Button background fills from bottom-to-top (green-300 → green-200)
- Arrow icon line shrinks
- Gap between label and icon increases


### Focus State

When the card receives keyboard focus:

- Black border outline around the card
- Button shows focus styling


### Pressed State

When the button is being pressed:

- Button returns to default styling momentarily


### Disabled State

When `disabled={true}`:

- Card is non-interactive
- Button shows disabled styling (gray background, no icon)
- Text colors muted
- Cursor changes to `not-allowed`


## Usage Examples

### Basic Card with Link

```tsx
import { CardImage } from 'shared/components/CardImage';

<CardImage
  image="/images/docs-hero.png"
  imageAlt="Documentation illustration"
  title="Documentation"
  subtitle="Access everything you need to get started working with the XRPL."
  buttonLabel="Get Started"
  href="/docs"
/>
```

### Card with Click Handler

```tsx
<CardImage
  image="/images/feature.png"
  imageAlt="Feature illustration"
  title="New Feature"
  subtitle="Learn about our latest feature and how it can help you build better applications."
  buttonLabel="Learn More"
  onClick={() => console.log('clicked')}
/>
```

### Disabled Card

```tsx
<CardImage
  image="/images/coming-soon.png"
  imageAlt="Coming soon"
  title="Coming Soon"
  subtitle="This feature is not yet available."
  buttonLabel="Unavailable"
  disabled
/>
```

### Card with Custom Class

```tsx
<CardImage
  image="/images/hero.png"
  imageAlt="Hero image"
  title="Custom Styled"
  subtitle="Card with additional custom styling."
  buttonLabel="Explore"
  href="/explore"
  className="my-custom-card"
/>
```

## Design Tokens

### Spacing

| Token | Value | Description |
|  --- | --- | --- |
| Image-to-content gap | 24px | Gap between image and content area |
| Title-to-subtitle gap | 12px | Gap between title and subtitle |
| Content horizontal padding | 8px | Left/right padding for content |
| Button margin-top | 30px | Button locked to bottom |
| Border radius | 16px | Card corner radius |


### Colors

| Element | Light Mode | Dark Mode |
|  --- | --- | --- |
| Card background | #FFFFFF | Gray 800 |
| Card border | Gray 300 (#CAD4DF) | Gray 700 |
| Image background | Gray 100 (#F0F3F7) | Gray 700 |
| Text color | #141414 | White |


### Typography

| Element | Token | Specs |
|  --- | --- | --- |
| Title | `.sh-md-l` | 28px/35px, -0.5px letter-spacing, light weight |
| Subtitle | `.body-l` | 18px/26.1px, -0.5px letter-spacing, light weight |


## How It Works

### Hover Animation

The card tracks hover state via React's `useState`. When hovered:

1. A `bds-card-image--hovered` class is added to the card
2. CSS rules target the nested Button component and apply hover styles
3. The button's `::before` pseudo-element animates (background fill)
4. The arrow icon line shrinks via `scaleX(0)`


This approach ensures the button animation triggers even when hovering areas of the card outside the button itself.

### Link Navigation

When `href` is provided:

- The entire card becomes clickable
- Clicking anywhere on the card navigates to the href
- The card is keyboard accessible (Enter/Space to activate)


### Button Integration

The component uses the existing BDS Button component with:

- `variant="primary"` - Solid green background
- `color="green"` - Green color theme


## Accessibility

### Keyboard Navigation

- **Tab**: Focus the card
- **Enter/Space**: Activate the card (navigate to href or trigger onClick)


### Screen Reader Support

- Card has appropriate `role="link"` when href is provided
- Image has descriptive alt text
- `aria-disabled` attribute for disabled state


## File Structure

```
shared/components/CardImage/
├── CardImage.tsx      # Component implementation
├── CardImage.scss     # Component styles
├── CardImage.md       # This documentation
└── index.ts           # Exports
```

## Related Components

- **Button**: Used internally for the CTA button
- **CardOffgrid**: Similar card component with different layout


## Browser Support

The component uses modern CSS features:

- CSS Grid/Flexbox
- CSS transforms and transitions
- `:focus-visible` pseudo-class
- `-webkit-line-clamp` for text truncation


All features are widely supported in modern browsers.