mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2026-09-27 23:47:59 +00:00
* 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>
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
validateButtonGroupwith a maximum of 2 buttons - Button colors are automatically adjusted based on the background variant
- The component uses
ButtonGrouppattern for consistent button styling