Files
xrpl-dev-portal/shared/components/CardOffgrid/CardOffgrid.md
Calvin 5021556c8e BDS component polish: link contrast, static cards, uniform CTAs, stacked hero (#3851)
* BDS component polish: link visibility, static cards, uniform CTAs

Component-level fixes and refinements across the 2026 brand components.

Accessibility / contrast
- StandardCard: pin description link colors. Every card variant has a light
  background in both themes, so theme-level link colors were wrong inside it —
  dark mode painted plain anchors white and BdsLink lilac-300, both of which
  wash out on a pale card. Links (including :visited) now use the card's own
  text color with an underline carrying the affordance; hover uses lilac-500,
  which clears 4.5:1 on all four card backgrounds.
- Breadcrumbs: point the open dropdown menu at the breadcrumb color tokens so
  the trail, trigger, and menu read as one color in both themes, and give dark
  mode gray-6 (7.41:1, matching light mode's 7.23:1).

Interaction
- CardOffgrid: drop every hover affordance when a card has neither href nor
  onClick. Static cards no longer get a pointer cursor, the color-wipe overlay,
  or a pressed state — the tokenization and trading carousels pass link-less
  cards and were advertising a click target that did not exist.
- CarouselFeatured: add a 'fade' transition alongside the default 'slide', for
  decks whose slides share a heading. Inactive slides are now inert, keeping
  focus and pointer events out of them in both styles.
- Add a bds-reduced-motion mixin so components can collapse motion to an
  instant state change.

Design consistency
- ButtonGroup: add forceVariant and forceNoPadding overrides, letting a section
  opt out of the count-based variant defaults and render one uniform treatment.
  Existing consumers are unaffected.
- FeatureTwoColumn: render every link as a tertiary button regardless of count,
  flush with the title and description.
- CardImage: render an all-bullet subtitle as a real <ul> so wrapped text hangs
  under the first character and screen readers announce it as a list.
- docs: give the node-installation card descriptions a paragraph break before
  their "Learn More" link.

Dependencies
- Bump @codemirror/state and view, add lang-javascript, lang-json, and lint,
  with overrides pinning state/view to one copy.

Docs updated alongside each component. Built CSS regenerated with the
production script to match the committed artifact's minified format.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ButtonGroup: don't strip padding from forced filled variants

`noPadding` was derived as `forceNoPadding || isMultiButton`, which was safe
while the 3+ block layout was always tertiary. `forceVariant` also accepts
`primary` and `secondary`, so a 3-button group forcing a filled variant had
`padding: 0 !important` (Button.scss) applied with no way to opt out.

Tie the implied no-padding to the resolved variant being tertiary. Behavior is
unchanged for every caller that doesn't pass `forceVariant`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Stacked hero variant, fade-by-default carousel, component docs

Layout
- HeaderHeroPrimaryMedia: add a `stacked` prop that puts the headline, subtitle,
  and buttons in one column (full width at base, 7/8 at md, 9/12 at lg) instead
  of the default headline-left / CTA-right split. The headline and CTA block
  bottom-align against each other in the two-column layout, so the stacked
  variant undoes that. Both arrangements now share the same headline and CTA
  elements, so they can't drift apart. Used on the docs landing hero.

Motion
- CarouselFeatured: make `fade` the default transition. Slides that share a
  heading are the common case, and a horizontal wipe drags the identical heading
  across the screen only to set it back down. `slide` is now opt-in for decks
  whose panels are genuinely distinct. Nudge the crossfade to 260ms in / 200ms
  out. The home page carousel opts into fade explicitly; developer-funding now
  inherits it.

Fixes
- CardTextIconCard: pass `headingAs` through to `cardContent`, which was
  accepting the prop but never receiving it.

Docs
- Expand the Button, CardImage, CardTextIcon, PageGrid, CalloutMediaBanner,
  LogoSquareGrid, and HeaderHeroPrimaryMedia references.

Built CSS regenerated with the production script.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:49:10 -07:00

458 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CardOffgrid Component - Usage Guide
## Overview
`CardOffgrid` is a feature highlight card component designed to showcase key capabilities, features, or resources. It combines an icon, title, and description in a visually engaging, interactive card format with smooth hover animations.
**Use CardOffgrid when:**
- Highlighting key features or capabilities
- Creating feature grids or showcases
- Linking to important documentation or resources
- Presenting product/service highlights
**Don't use CardOffgrid for:**
- Simple content cards (use standard Bootstrap cards)
- Navigation items (use navigation components)
- Data display (use tables or data components)
- Long-form content (use article/page layouts)
---
## When to Use Each Variant
### Neutral Variant (`variant="neutral"`)
**Use for:**
- General feature highlights
- Standard content cards
- Secondary or supporting features
- When you want subtle, professional presentation
**Example use cases:**
- Documentation sections
- Feature lists
- Service offerings
- Standard informational cards
### Green Variant (`variant="green"`)
**Use for:**
- Primary or featured highlights
- Call-to-action cards
- Important announcements
- Brand-emphasized content
**Example use cases:**
- Hero feature cards
- Primary CTAs
- Featured resources
- Branded highlights
---
## Content Best Practices
### Title Guidelines
✅ **Do:**
- Keep titles concise (1-3 words ideal)
- Use line breaks (`\n`) for multi-word titles when needed
- Make titles action-oriented or descriptive
- Examples: "Onchain Metadata", "Token\nManagement", "Cross-Chain\nBridges"
❌ **Don't:**
- Write long sentences as titles
- Use more than 2 lines
- Include punctuation (periods, commas)
- Make titles too generic ("Feature", "Service")
### Description Guidelines
✅ **Do:**
- Write 1-2 sentences (15-25 words ideal)
- Focus on benefits or key information
- Use clear, simple language
- Keep descriptions scannable
❌ **Don't:**
- Write paragraphs (save for full pages)
- Use jargon without context
- Include multiple ideas in one description
- Make descriptions too short (< 10 words) or too long (> 40 words)
### Icon Guidelines
✅ **Do:**
- Use SVG icons for crisp rendering
- Choose icons that represent the feature clearly
- Ensure icons are recognizable at 68×68px
- Use consistent icon style across cards
❌ **Don't:**
- Use low-resolution raster images
- Choose overly complex icons
- Mix icon styles within a single grid
- Use icons that don't relate to the content
---
## Interaction Patterns
### Using `onClick` vs `href`
**Use `onClick` when:**
- Triggering JavaScript actions (modals, analytics, state changes)
- Opening external links in new tabs
- Performing client-side navigation
- Handling complex interactions
```tsx
<CardOffgrid
variant="neutral"
icon={<AnalyticsIcon />}
title="View Analytics"
description="See detailed usage statistics and insights."
onClick={() => {
trackEvent('analytics_viewed');
openModal('analytics');
}}
/>
```
**Use `href` when:**
- Navigating to internal pages
- Linking to documentation
- Simple page navigation
- SEO-friendly links
```tsx
<CardOffgrid
variant="green"
icon="/icons/docs.svg"
title="API\nReference"
description="Complete API documentation and examples."
href="/docs/api"
/>
```
### Static (Presentational) Cards
Omit both `onClick` and `href` to render a purely presentational card. It has no
click target, so the component drops every interactive affordance: no pointer
cursor, no hover color wipe, and no pressed state. It renders as a plain `<div>`
and isn't focusable or announced as a control.
Use this for cards that only communicate information — e.g. a "why XRPL" feature
carousel where none of the cards link anywhere.
```tsx
<CardOffgrid
variant="green"
icon="/icons/metadata.svg"
title="Onchain Metadata"
description="Easily store key asset information."
/>
```
### Disabled State
Use `disabled` when:
- Feature is coming soon
- Feature requires authentication
- Feature is temporarily unavailable
- You want to show but not allow interaction
```tsx
<CardOffgrid
variant="neutral"
icon={<BetaIcon />}
title="Coming\nSoon"
description="This feature will be available in the next release."
disabled
/>
```
---
## Layout Best Practices
### Grid Layouts
**Recommended grid patterns:**
```tsx
// 2-column grid (desktop)
<div className="row">
<div className="col-md-6 mb-4">
<CardOffgrid {...props1} />
</div>
<div className="col-md-6 mb-4">
<CardOffgrid {...props2} />
</div>
</div>
// 3-column grid (desktop)
<div className="row">
{cards.map(card => (
<div key={card.id} className="col-md-4 mb-4">
<CardOffgrid {...card} />
</div>
))}
</div>
```
**Spacing:**
- Use Bootstrap spacing utilities (`mb-4`, `mb-5`) between cards
- Maintain consistent spacing in grids
- Cards are responsive and stack on mobile automatically
### Single Card Usage
For hero sections or featured highlights:
```tsx
<div className="d-flex justify-content-center">
<CardOffgrid
variant="green"
icon={<FeaturedIcon />}
title="New Feature"
description="Introducing our latest capability..."
href="/features/new"
/>
</div>
```
---
## Accessibility Best Practices
### Semantic HTML
The component automatically renders as:
- `<button>` when using `onClick`
- `<a>` when using `href`
- `<div>` when neither is provided (static card — not focusable, not announced as a control)
This ensures proper semantic meaning for screen readers.
### Keyboard Navigation
✅ **Always test:**
- Tab navigation moves focus to cards
- Enter/Space activates cards
- Focus ring is clearly visible
- Focus order follows logical reading order
### Screen Reader Content
✅ **Ensure:**
- Titles are descriptive and unique
- Descriptions provide context
- Icons have appropriate `aria-hidden="true"` (handled automatically)
- Disabled cards communicate their state
### Color Contrast
All variants meet WCAG AA standards:
- Dark mode: White text on colored backgrounds
- Light mode: Dark text on light backgrounds
- Focus rings provide sufficient contrast
---
## Common Patterns
### Feature Showcase Grid
```tsx
const features = [
{
variant: 'green',
icon: <TokenIcon />,
title: 'Token\nManagement',
description: 'Create and manage fungible and non-fungible tokens.',
href: '/docs/tokens'
},
{
variant: 'neutral',
icon: <MetadataIcon />,
title: 'Onchain\nMetadata',
description: 'Store key asset information using simple APIs.',
href: '/docs/metadata'
},
// ... more features
];
<div className="row">
{features.map((feature, index) => (
<div key={index} className="col-md-4 mb-4">
<CardOffgrid {...feature} />
</div>
))}
</div>
```
### Mixed Variants for Hierarchy
Use green variant for primary features, neutral for supporting:
```tsx
<div className="row">
<div className="col-md-6 mb-4">
<CardOffgrid
variant="green" // Primary feature
icon={<PrimaryIcon />}
title="Main Feature"
description="Our flagship capability..."
href="/feature/main"
/>
</div>
<div className="col-md-6 mb-4">
<CardOffgrid
variant="neutral" // Supporting feature
icon={<SupportIcon />}
title="Supporting Feature"
description="Complementary capability..."
href="/feature/support"
/>
</div>
</div>
```
### Coming Soon Pattern
```tsx
<CardOffgrid
variant="neutral"
icon={<ComingSoonIcon />}
title="Coming\nSoon"
description="This feature is currently in development and will be available soon."
disabled
/>
```
---
## Performance Considerations
### Icon Optimization
✅ **Best practices:**
- Use SVG React components (inlined) for small icons
- Use optimized SVG files for image icons
- Avoid large raster images
- Consider lazy loading for below-the-fold cards
### Rendering Performance
- Cards are lightweight components
- Hover animations use CSS transforms (GPU-accelerated)
- No heavy JavaScript calculations
- Suitable for grids with 10+ cards
---
## Troubleshooting
### Common Issues
**Card not clickable:**
- Ensure `onClick` or `href` is provided
- Check that `disabled` is not set to `true`
- Verify no parent element is blocking pointer events
**Icon not displaying:**
- Verify icon path is correct (if using string)
- Check icon component is properly imported
- Ensure icon fits within 68×68px bounds
**Hover animation not working:**
- Confirm the card has an `onClick` or `href` — static cards intentionally have no hover animation
- Check browser supports CSS `clip-path`
- Verify no conflicting CSS is overriding transitions
- Test in different browsers
**Focus ring not visible:**
- Ensure keyboard navigation (Tab key)
- Check focus ring color contrasts with background
- Verify `outline-offset: 2px` is applied
---
## Design System Integration
### Color Tokens
All colors reference `styles/_colors.scss`:
- Dark mode (default): Uses `$gray-500`, `$gray-400`, `$green-300`, `$green-200`
- Light mode (`html.light`): Uses `$gray-200`, `$gray-300`, `$green-200`, `$green-300`
### Typography
- Title: Booton Light, 32px, -1px letter-spacing
- Description: Booton Light, 18px, -0.5px letter-spacing
### Spacing
- Card padding: 24px
- Content gap: 40px (between title and description)
- Icon container: 84×84px
---
## Figma References
- **Light Mode Colors**: [Figma Design - Light Mode](https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8001-1963&m=dev)
- **Dark Mode Colors**: [Figma Design - Dark Mode](https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8001-2321&m=dev)
- **Animation Specs**: [Figma Design - Storyboard](https://www.figma.com/design/vwDwMJ3mFrAklj5zvZwX5M/Card---OffGrid?node-id=8007-1096&m=dev)
---
## Component API
```typescript
interface CardOffgridProps {
/** Color variant: 'neutral' (default) or 'green' */
variant?: 'neutral' | 'green';
/** Icon element (ReactNode) or image path (string) */
icon: React.ReactNode | string;
/** Card title - use \n for line breaks */
title: string;
/** Card description text (1-2 sentences) */
description: string;
/** Click handler - renders as <button> */
onClick?: () => void;
/** Link destination - renders as <a> */
href?: string;
/** Disabled state - prevents interaction */
disabled?: boolean;
/** Additional CSS classes */
className?: string;
}
```
---
## Quick Reference
| Use Case | Variant | Interaction |
|----------|---------|-------------|
| Standard feature | `neutral` | `href` or `onClick` |
| Primary feature | `green` | `href` or `onClick` |
| Coming soon | `neutral` | `disabled` |
| Feature grid | Mix both | `href` preferred |
| Hero section | `green` | `href` |
---
## Examples
See the [CardOffgrid Showcase](/about/card-offgrid-showcase) for live examples and interactive demos.