Files
xrpl-dev-portal/shared/patterns/CarouselFeatured
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
..

CarouselFeatured

A featured image carousel pattern with a two-column layout on desktop (image left, content right) and single-column layout on tablet/mobile (content top, image bottom). Features a heading, feature list with dividers, optional buttons, and navigation controls.

Usage

import { CarouselFeatured } from '@/shared/patterns/CarouselFeatured';

<CarouselFeatured
  slides={[
    {
      id: 1,
      heading: "Powered by Developers",
      features: [
        { title: "Easy-to-Integrate APIs", description: "Build with common languages..." },
        { title: "Full Lifecycle Support", description: "From dev tools to deployment..." },
      ],
      buttons: [
        { label: "Get Started", href: "/docs" },
        { label: "Learn More", href: "/about" }
      ],
      imageSrc: '/image1.jpg',
      imageAlt: 'Slide 1'
    },
  ]}
  background="grey"
/>

Props

Required Props

Prop Type Description
slides CarouselSlide[] Array of slides to display in the carousel

Optional Props

Prop Type Default Description
background 'grey' | 'neutral' | 'yellow' 'grey' Background color variant
transition 'slide' | 'fade' 'fade' Slide transition style. See Transitions

Transitions

Value Behavior
'fade' (default) Slides crossfade in place with no lateral movement. The image and heading lead, then the feature list, then the buttons, each on a 60ms stagger. Reads best when slides share a heading and only the supporting content changes — a horizontal wipe would drag the identical heading across the screen and set it back down where it started.
'slide' Slides wipe horizontally. Opt into this when each slide is a genuinely distinct panel and the lateral movement communicates that.

Both styles collapse to an instant swap under prefers-reduced-motion: reduce.

'fade' stacks every slide in a single CSS grid cell, so the carousel still sizes to the tallest slide and won't change height between slides.

Type Definitions

CarouselSlide

interface CarouselSlide {
  id: string | number;  // Unique identifier for the slide
  heading: string;      // Heading text for the slide
  features: CarouselFeatureItem[]; // Slide feature list
  buttons?: ButtonConfig[]; // Optional slide buttons
  imageSrc: string;     // Image source URL
  imageAlt: string;     // Alt text for the image
}

CarouselFeatureItem

interface CarouselFeatureItem {
  title: React.ReactNode;        // Feature title node
  description: React.ReactNode;  // Feature description node
}

ButtonConfig

interface ButtonConfig {
  label: string;        // Button text
  href?: string;        // Optional link URL
  onClick?: () => void; // Optional click handler
  forceColor?: boolean; // Force button color override
}

Background Variants

The component supports three background variants that adapt to light/dark mode:

  • grey (default): Light mode: gray-200 (#E6EAF0), Dark mode: gray-300 (#CAD4DF)
  • neutral: Light mode: white (#FFF), Dark mode: black (#141414)
  • yellow: Light mode: yellow-100 (#F3F1EB), Dark mode: yellow-100 (#F3F1EB)

Features

  • Responsive Layout: Two-column on desktop (lg+), single-column on mobile/tablet
  • Image Carousel: Navigate through multiple slides with prev/next buttons
  • Auto-hide Navigation: Navigation buttons automatically hide when only one slide is present
  • Feature List: Display multiple features with dividers
  • Button Group: Supports 1-2 buttons with validation
  • Background Variants: Three color options with light/dark mode support
  • Accessibility: Proper ARIA labels for navigation buttons

Layout Behavior

Desktop (lg+)

  • Image column on the left (6 columns)
  • Content column on the right (6 columns)
  • Navigation buttons in header (desktop variant)

Tablet/Mobile

  • Content section at the top
  • Image section at the bottom
  • Navigation buttons in CTA section (mobile variant)

Examples

See the showcase page for live examples with different configurations.

Notes

  • Navigation buttons are automatically hidden when slides.length === 1
  • Buttons are validated using validateButtonGroup with a maximum of 2 buttons
  • Button colors are automatically adjusted based on the background variant
  • The component uses ButtonGroup pattern for consistent button styling