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

11 KiB
Raw Blame History

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
<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
<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.

<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
<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:

// 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:

<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

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:

<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

<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


Component API

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 for live examples and interactive demos.