diff --git a/about/carousel-card-list-showcase.page.tsx b/about/carousel-card-list-showcase.page.tsx new file mode 100644 index 000000000..1669d915e --- /dev/null +++ b/about/carousel-card-list-showcase.page.tsx @@ -0,0 +1,608 @@ +import { PageGrid, PageGridRow, PageGridCol } from "shared/components/PageGrid/page-grid"; +import { CarouselCardList } from "shared/patterns/CarouselCardList"; +import { Divider } from "shared/components/Divider"; + +export const frontmatter = { + seo: { + title: 'CarouselCardList Pattern Showcase', + description: "A comprehensive showcase of the CarouselCardList pattern component demonstrating horizontal scrolling, navigation buttons, and color variants in the XRPL.org Design System.", + } +}; + +// Sample icon components for demonstration +const TokenIcon = () => ( + + + + +); + +const WalletIcon = () => ( + + + + + +); + +const ChartIcon = () => ( + + + + +); + +const ShieldIcon = () => ( + + + + +); + +const GlobeIcon = () => ( + + + + + +); + +const CodeIcon = () => ( + + + + + +); + +// Sample cards data for neutral variant +const neutralCards = [ + { + icon: , + title: "Native\nTokenization", + description: "Issue and manage digital assets directly on the ledger without smart contracts.", + href: "#tokenization", + }, + { + icon: , + title: "Low Cost\nTransactions", + description: "Transaction costs are a fraction of a cent, making microtransactions viable.", + href: "#low-cost", + }, + { + icon: , + title: "Built-in\nDEX", + description: "Trade any token for any other token using the native decentralized exchange.", + href: "#dex", + }, + { + icon: , + title: "Enterprise\nSecurity", + description: "Multi-signature support and advanced key management for institutional needs.", + href: "#security", + }, + { + icon: , + title: "Global\nReach", + description: "Connect to a worldwide network of validators in seconds, not minutes.", + href: "#global", + }, + { + icon: , + title: "Developer\nFriendly", + description: "Comprehensive SDKs and APIs for JavaScript, Python, Java, and more.", + href: "#developer", + }, +]; + +// Sample cards data for green variant +const greenCards = [ + { + icon: , + title: "Stablecoin\nIssuance", + description: "Create and manage compliant stablecoins with built-in freeze and clawback capabilities.", + href: "#stablecoin", + }, + { + icon: , + title: "Institutional\nCustody", + description: "Multi-signature accounts and escrow features for enterprise-grade custody solutions.", + href: "#custody", + }, + { + icon: , + title: "Real-Time\nSettlement", + description: "Transactions settle in 3-5 seconds with finality, enabling real-time payments.", + href: "#settlement", + }, + { + icon: , + title: "Regulatory\nCompliance", + description: "Built-in features for AML/KYC compliance and regulatory reporting requirements.", + href: "#compliance", + }, + { + icon: , + title: "Cross-Border\nPayments", + description: "Seamless international transfers without correspondent banking delays.", + href: "#cross-border", + }, +]; + +export default function CarouselCardListShowcase() { + return ( +
+
+ {/* Hero Section */} +
+
+
Pattern Showcase
+

CarouselCardList Pattern

+

+ A horizontal scrolling carousel that displays CardOffgrid components with navigation buttons. + Supports neutral and green color variants, responsive sizing, and dark/light mode theming. +

+
+
+ + {/* Feature Overview */} + + + +

Features

+
+
+
Layout
+
    +
  • Horizontal scrolling cards
  • +
  • Navigation buttons (prev/next)
  • +
  • Title constrained to grid
  • +
  • Hidden scrollbar
  • +
+
+
+
Responsive Behavior
+
    +
  • Mobile: 343×400px cards
  • +
  • Tablet: 356×440px cards
  • +
  • Desktop: 400×480px cards
  • +
+
+
+
Button States
+
    +
  • Enabled / Disabled
  • +
  • Hover / Active states
  • +
  • Focus ring for keyboard nav
  • +
+
+
+
Theming
+
    +
  • Dark mode (default)
  • +
  • Light mode (html.light)
  • +
  • Neutral & Green card variants
  • +
  • Independent button colors (neutral, green, black)
  • +
+
+
+
+
+
+ + + + {/* Neutral Cards + Neutral Buttons (Default) */} +
+ + + +

Neutral Cards + Neutral Buttons (Default)

+

+ variant="neutral" - Gray cards with matching gray navigation buttons. +

+
+
+
+ +
+ + + + {/* Neutral Cards + Green Buttons */} +
+ + + +

Neutral Cards + Green Buttons

+

+ variant="neutral" buttonVariant="green" - Gray cards with green navigation buttons. +

+
+
+
+ +
+ + + + {/* Neutral Cards + Black Buttons */} +
+ + + +

Neutral Cards + Black Buttons

+

+ variant="neutral" buttonVariant="black" - Gray cards with black navigation buttons. +

+
+
+
+ +
+ + + + {/* Green Cards + Green Buttons */} +
+ + + +

Green Cards + Green Buttons

+

+ variant="green" buttonVariant="green" - Green cards with matching green navigation buttons. +

+
+
+
+ +
+ + + + {/* Green Cards + Black Buttons */} +
+ + + +

Green Cards + Black Buttons

+

+ variant="green" buttonVariant="black" - Green cards with black navigation buttons. +

+
+
+
+ +
+ + + + {/* Navigation Buttons */} + + + +

Navigation Button Specifications

+ +
+
+
Dimensions
+
    +
  • Desktop: 40px × 40px
  • +
  • Tablet/Mobile: 37px × 37px
  • +
  • Gap: 8px between buttons
  • +
+
+
+
Neutral Colors (Dark Mode)
+
    +
  • Enabled: $gray-500 (#72777E)
  • +
  • Hover: $gray-400 (#8A919A)
  • +
  • Disabled: $gray-500 @ 50%
  • +
+
+
+
Green Colors (Dark Mode)
+
    +
  • Enabled: $green-300 (#21E46B)
  • +
  • Hover: $green-200 (#70EE97)
  • +
  • Disabled: $green-500 @ 50%
  • +
+
+
+
Black Colors (Dark Mode)
+
    +
  • Enabled: $black (#141414)
  • +
  • Hover: $gray-500 (#72777E)
  • +
  • Disabled: $black @ 50%
  • +
+
+
+
+
+
+ + + + {/* Spacing Tokens */} + + + +

Spacing Tokens

+ +
+
+
Token
+
Mobile
+
Tablet
+
Desktop
+
+ +
+
Header Gap
+
8px
+
8px
+
16px
+
+ + +
+
Section Gap
+
24px
+
32px
+
40px
+
+ + +
+
Cards Gap
+
8px
+
8px
+
8px
+
+ + +
+
Card Dimensions
+
343×400px
+
356×440px
+
400×480px
+
+ + +
+
Card Padding
+
16px
+
20px
+
24px
+
+
+
+
+
+ + + + {/* API Reference */} + + + +

Component API

+ +
CarouselCardListProps
+
+
+
Prop
+
Type
+
Default
+
Description
+
+ +
+
variant
+
'neutral' | 'green'
+
'neutral'
+
Color variant for cards
+
+ + +
+
buttonVariant
+
'neutral' | 'green' | 'black'
+
'neutral'
+
Color variant for navigation buttons (independent of cards)
+
+ + +
+
heading
+
ReactNode
+
required
+
Section heading text
+
+ + +
+
description
+
ReactNode
+
required
+
Section description text
+
+ + +
+
cards
+
CarouselCardConfig[]
+
required
+
Array of card configurations
+
+
+ +
CarouselCardConfig
+

Each card in the cards array accepts the following properties (same as CardOffgrid, without variant):

+
+
+
Prop
+
Type
+
Required
+
Description
+
+ +
+
icon
+
ReactNode | string
+
Yes
+
Icon component or image URL
+
+ + +
+
title
+
string
+
Yes
+
Card title (use \n for line breaks)
+
+ + +
+
description
+
string
+
Yes
+
Card description text
+
+ + +
+
href
+
string
+
No
+
Link destination URL
+
+ + +
+
onClick
+
() => void
+
No
+
Click handler function
+
+ + +
+
disabled
+
boolean
+
No
+
Disabled state
+
+
+
+
+
+ + + + {/* Usage Example */} + + + +

Usage Example

+
+
+{`import { CarouselCardList } from 'shared/patterns/CarouselCardList';
+
+// Basic usage - button color matches card color by default
+,
+      title: "Native\\nTokenization",
+      description: "Issue and manage digital assets directly on the ledger.",
+      href: "/docs/tokenization",
+    },
+    // ... more cards
+  ]}
+/>
+
+// With independent button color
+`}
+                
+
+
+
+
+ + + + {/* Design References */} + + + +

Design References

+
+
+ Main Carousel Design:{' '} + + Section Carousel - Card List (Figma) + +
+
+ Button States:{' '} + + Carousel Button States (Figma) + +
+
+ Component Location:{' '} + shared/patterns/CarouselCardList/ +
+
+ Documentation:{' '} + shared/patterns/CarouselCardList/CarouselCardList.md +
+
+
+
+
+
+
+ ); +} + diff --git a/shared/patterns/CarouselCardList/CarouselCardList.md b/shared/patterns/CarouselCardList/CarouselCardList.md new file mode 100644 index 000000000..58f826cba --- /dev/null +++ b/shared/patterns/CarouselCardList/CarouselCardList.md @@ -0,0 +1,175 @@ +# CarouselCardList Pattern + +A horizontal scrolling carousel pattern that displays `CardOffgrid` components with navigation buttons. Features responsive sizing, smooth scrolling, and full dark/light mode theming support. + +## Features + +- Horizontal scrolling card carousel with navigation buttons +- Automatic button enable/disable based on scroll position +- Two color variants: `neutral` and `green` (inherited by cards and buttons) +- Responsive card sizing across mobile, tablet, and desktop breakpoints +- Full dark mode and light mode support +- Heading and description constrained to page grid +- Hidden scrollbar with smooth scroll behavior +- Keyboard navigation and accessibility support + +## Usage + +```tsx +import { CarouselCardList } from 'shared/patterns/CarouselCardList'; + +, + title: "Native\nTokenization", + description: "Issue and manage digital assets directly on the ledger.", + href: "/docs/tokenization", + }, + { + icon: , + title: "Low Cost\nTransactions", + description: "Transaction costs are a fraction of a cent.", + href: "/docs/fees", + }, + // ... more cards + ]} +/> +``` + +## Props + +### CarouselCardListProps + +| Prop | Type | Required | Default | Description | +|------|------|----------|---------|-------------| +| `variant` | `'neutral' \| 'green'` | No | `'neutral'` | Color variant for cards and navigation buttons | +| `heading` | `ReactNode` | Yes | - | Section heading text | +| `description` | `ReactNode` | Yes | - | Section description text | +| `cards` | `CarouselCardConfig[]` | Yes | - | Array of card configurations | +| `className` | `string` | No | - | Additional CSS classes for the section | + +### CarouselCardConfig + +Each card in the `cards` array accepts the following properties (same as `CardOffgridProps`, excluding `variant`): + +| Prop | Type | Required | Description | +|------|------|----------|-------------| +| `icon` | `ReactNode \| string` | Yes | Icon component or image URL | +| `title` | `string` | Yes | Card title (use `\n` for line breaks) | +| `description` | `string` | Yes | Card description text | +| `href` | `string` | No | Link destination URL | +| `onClick` | `() => void` | No | Click handler function | +| `disabled` | `boolean` | No | Disabled state | +| `className` | `string` | No | Additional CSS classes | + +## Variants + +### Neutral Variant + +The default variant using gray color palette. Best for general purpose content sections. + +```tsx + +``` + +### Green Variant + +Uses the brand green color palette. Best for featured or highlighted sections. + +```tsx + +``` + +## Responsive Behavior + +| Breakpoint | Card Width | Card Height | Card Padding | Button Size | +|------------|------------|-------------|--------------|-------------| +| Mobile (< 576px) | 343px | 400px | 16px | 37px | +| Tablet (576px - 991px) | 356px | 440px | 20px | 37px | +| Desktop (≥ 992px) | 400px | 480px | 24px | 40px | + +### Spacing Tokens + +| Token | Mobile | Tablet | Desktop | +|-------|--------|--------|---------| +| Header gap | 8px | 8px | 16px | +| Section gap | 24px | 32px | 40px | +| Cards gap | 8px | 8px | 8px | + +## Accessibility + +- Navigation buttons have descriptive `aria-label` attributes ("Previous cards", "Next cards") +- Carousel track has `role="region"` with `aria-label="Card carousel"` +- Keyboard navigation supported via Tab and arrow keys +- Focus ring visible on keyboard navigation (uses `focus-visible`) +- Disabled buttons properly convey state via `disabled` attribute and visual styling +- Cards support keyboard interaction (Tab, Enter, Space) + +## Design Tokens + +### Colors (Dark Mode - Default) + +**Neutral Variant:** +- Button Enabled: `$gray-300` (#CAD4DF) +- Button Hover: `$gray-400` (#8A919A) +- Button Disabled: `$gray-500` @ 50% opacity + +**Green Variant:** +- Button Enabled: `$green-300` (#21E46B) +- Button Hover: `$green-200` (#70EE97) +- Button Disabled: `$green-100` (#ACFFC5) + +### Colors (Light Mode - `html.light`) + +**Neutral Variant:** +- Button Enabled: `$gray-300` (#CAD4DF) +- Button Hover: `$gray-400` (#8A919A) +- Button Disabled: `$gray-100` (#F0F3F7) + +**Green Variant:** +- Button Enabled: `$green-300` (#21E46B) +- Button Hover: `$green-200` (#70EE97) +- Button Disabled: `$green-100` (#ACFFC5) + +## CSS Classes + +| Class | Description | +|-------|-------------| +| `.bds-carousel-card-list` | Base section container | +| `.bds-carousel-card-list--neutral` | Neutral color variant | +| `.bds-carousel-card-list--green` | Green color variant | +| `.bds-carousel-card-list__header` | Header wrapper (title, subtitle, nav) | +| `.bds-carousel-card-list__header-content` | Title and subtitle wrapper | +| `.bds-carousel-card-list__heading` | Section heading (uses `.h-md`) | +| `.bds-carousel-card-list__description` | Section description (uses `.body-l`) | +| `.bds-carousel-card-list__nav` | Navigation buttons wrapper | +| `.bds-carousel-card-list__button` | Navigation button | +| `.bds-carousel-card-list__button--prev` | Previous button modifier | +| `.bds-carousel-card-list__button--disabled` | Disabled button modifier | +| `.bds-carousel-card-list__track-wrapper` | Scroll container wrapper | +| `.bds-carousel-card-list__track` | Horizontal scroll track | +| `.bds-carousel-card-list__card` | Individual card wrapper | + +## Showcase + +View the pattern showcase at: `/about/carousel-card-list-showcase` + +## Design References + +- **Main Design:** [Section Carousel - Card List (Figma)](https://www.figma.com/design/w0CVv1c40nWDRD27mLiMWS/Section-Carousel---Card-List?node-id=15055-3730&m=dev) +- **Button States:** [Carousel Button States (Figma)](https://www.figma.com/design/w0CVv1c40nWDRD27mLiMWS/Section-Carousel---Card-List?node-id=15055-1033&m=dev) + diff --git a/shared/patterns/CarouselCardList/CarouselCardList.scss b/shared/patterns/CarouselCardList/CarouselCardList.scss new file mode 100644 index 000000000..d90c3869d --- /dev/null +++ b/shared/patterns/CarouselCardList/CarouselCardList.scss @@ -0,0 +1,514 @@ +// BDS CarouselCardList Pattern Styles +// Brand Design System - Horizontal scrolling carousel with CardOffgrid components +// +// Naming Convention: BEM with 'bds' namespace +// .bds-carousel-card-list - Base section container +// .bds-carousel-card-list--neutral - Neutral color variant +// .bds-carousel-card-list--green - Green color variant +// .bds-carousel-card-list__header - Header wrapper (title, subtitle, nav) +// .bds-carousel-card-list__header-content - Title and subtitle wrapper +// .bds-carousel-card-list__heading - Section heading (uses .h-md) +// .bds-carousel-card-list__description - Section description (uses .body-l) +// .bds-carousel-card-list__nav - Navigation buttons wrapper +// .bds-carousel-card-list__button - Navigation button +// .bds-carousel-card-list__track-wrapper - Scroll container wrapper +// .bds-carousel-card-list__track - Horizontal scroll track +// .bds-carousel-card-list__card - Individual card wrapper + +// ============================================================================= +// Design Tokens (from Figma) +// ============================================================================= + +$bds-grid-gutter: 8px; + +// Grid padding (matches PageGrid container padding) +$bds-carousel-grid-padding-sm: 18px; // Mobile +$bds-carousel-grid-padding-md: 24px; // Tablet +$bds-carousel-grid-padding-lg: 32px; // Desktop (lg+) +$bds-carousel-grid-max-width: 1280px; // Max container width (per _breakpoints.scss $xl) + +// Spacing - Header gap (between heading and description) +$bds-carousel-header-gap-sm: 8px; // Mobile +$bds-carousel-header-gap-md: 8px; // Tablet +$bds-carousel-header-gap-lg: 16px; // Desktop + +// Spacing - Section gap (between header content and buttons row on mobile) +$bds-carousel-section-gap-sm: 24px; // Mobile +$bds-carousel-section-gap-md: 32px; // Tablet +$bds-carousel-section-gap-lg: 40px; // Desktop + +// Spacing - Gap between header and cards +$bds-carousel-cards-gap-sm: 24px; // Mobile +$bds-carousel-cards-gap-md: 32px; // Tablet +$bds-carousel-cards-gap-lg: 40px; // Desktop + +// Button dimensions +$bds-carousel-button-size-sm: 37px; // Mobile/Tablet +$bds-carousel-button-size-lg: 40px; // Desktop +$bds-carousel-button-gap: 8px; + +// Card dimensions per breakpoint +$bds-carousel-card-width-sm: 343px; // Mobile +$bds-carousel-card-height-sm: 400px; +$bds-carousel-card-width-md: 356px; // Tablet +$bds-carousel-card-height-md: 440px; +$bds-carousel-card-width-lg: 400px; // Desktop +$bds-carousel-card-height-lg: 480px; + +// Card padding per breakpoint +$bds-carousel-card-padding-sm: 16px; +$bds-carousel-card-padding-md: 20px; +$bds-carousel-card-padding-lg: 24px; + +// Transition +$bds-carousel-transition: 200ms cubic-bezier(0.98, 0.12, 0.12, 0.98); + +// ============================================================================= +// Section Container +// ============================================================================= + +.bds-carousel-card-list { + width: 100%; + // Constrain to max-width at xl breakpoint (per _breakpoints.scss) + @include media-breakpoint-up(xl) { + max-width: $bds-carousel-grid-max-width; + margin-left: auto; + margin-right: auto; + } + // Allow focus rings to be visible (no overflow:hidden) +} + +// ============================================================================= +// Header Section +// ============================================================================= + +.bds-carousel-card-list__header { + display: flex; + flex-direction: column; + gap: $bds-carousel-section-gap-sm; + // Apply same padding as track to align header with cards + padding-left: $bds-carousel-grid-padding-sm; + padding-right: $bds-carousel-grid-padding-sm; + + @include media-breakpoint-up(md) { + gap: $bds-carousel-section-gap-md; + padding-left: $bds-carousel-grid-padding-md; + padding-right: $bds-carousel-grid-padding-md; + } + + // Row layout only at desktop (lg and up) + @include media-breakpoint-up(lg) { + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + gap: $bds-carousel-section-gap-lg; + padding-left: $bds-carousel-grid-padding-lg; + padding-right: $bds-carousel-grid-padding-lg; + } +} + +.bds-carousel-card-list__header-content { + display: flex; + flex-direction: column; + gap: $bds-carousel-header-gap-sm; + // Full width on mobile and tablet + max-width: 100%; + + @include media-breakpoint-up(md) { + gap: $bds-carousel-header-gap-md; + } + + // Constrain heading/description to grid (8 columns at desktop) + @include media-breakpoint-up(lg) { + gap: $bds-carousel-header-gap-lg; + max-width: 808px; // Desktop: 8 columns + } +} + +.bds-carousel-card-list__heading { + margin: 0; + // Typography handled by .h-md class from _font.scss +} + +.bds-carousel-card-list__description { + margin: 0; + // Typography handled by .body-l class from _font.scss +} + +// ============================================================================= +// Navigation Buttons Container +// ============================================================================= + +.bds-carousel-card-list__nav { + display: flex; + gap: $bds-carousel-button-gap; + justify-content: flex-end; + flex-shrink: 0; + // Add padding to allow focus ring to be visible without clipping + padding: 4px; + margin: -4px; +} + +// ============================================================================= +// Navigation Buttons +// ============================================================================= + +.bds-carousel-card-list__button { + // Reset button styles + appearance: none; + border: none; + background: none; + padding: 0; + margin: 0; + cursor: pointer; + + // Layout + display: flex; + align-items: center; + justify-content: center; + width: $bds-carousel-button-size-sm; + height: $bds-carousel-button-size-sm; + + // Transition + transition: background-color $bds-carousel-transition, + opacity $bds-carousel-transition; + + @include media-breakpoint-up(lg) { + width: $bds-carousel-button-size-lg; + height: $bds-carousel-button-size-lg; + } + + // Focus styles + &:focus { + outline: 2px solid $white; + outline-offset: 2px; + } + + &:focus:not(:focus-visible) { + outline: none; + } + + &:focus-visible { + outline: 2px solid $white; + outline-offset: 2px; + } +} + +// Arrow icon +.bds-carousel-card-list__arrow-icon { + width: 18px; + height: 16px; + + @include media-breakpoint-down(lg) { + width: 18px; + height: 15px; + } +} + +// ============================================================================= +// Scroll Track +// ============================================================================= + +.bds-carousel-card-list__track-wrapper { + margin-top: $bds-carousel-cards-gap-sm; + overflow: visible; + // Add left padding here so it's OUTSIDE the scrollable area + padding-left: $bds-carousel-grid-padding-sm; + + @include media-breakpoint-up(md) { + margin-top: $bds-carousel-cards-gap-md; + padding-left: $bds-carousel-grid-padding-md; + } + + @include media-breakpoint-up(lg) { + margin-top: $bds-carousel-cards-gap-lg; + padding-left: $bds-carousel-grid-padding-lg; + } +} + +.bds-carousel-card-list__track { + display: flex; + gap: $bds-grid-gutter; + overflow-x: auto; + overflow-y: visible; + scroll-snap-type: x mandatory; + scroll-behavior: smooth; + -webkit-overflow-scrolling: touch; + + // Hide scrollbar but keep functionality + scrollbar-width: none; + -ms-overflow-style: none; + + &::-webkit-scrollbar { + display: none; + } + + // Vertical padding to prevent focus ring clipping + padding-top: 4px; + padding-bottom: 4px; + margin-top: -4px; + margin-bottom: -4px; + + // Focus outline for keyboard navigation + &:focus { + outline: none; + } + + &:focus-visible { + outline: 2px solid $white; + outline-offset: 4px; + } +} + +// ============================================================================= +// Card Wrapper +// ============================================================================= + +.bds-carousel-card-list__card { + flex-shrink: 0; + scroll-snap-align: start; + + // Override CardOffgrid dimensions for carousel + .bds-card-offgrid { + width: $bds-carousel-card-width-sm; + height: $bds-carousel-card-height-sm; + padding: $bds-carousel-card-padding-sm; + + @include media-breakpoint-up(md) { + width: $bds-carousel-card-width-md; + height: $bds-carousel-card-height-md; + padding: $bds-carousel-card-padding-md; + } + + @include media-breakpoint-up(lg) { + width: $bds-carousel-card-width-lg; + height: $bds-carousel-card-height-lg; + padding: $bds-carousel-card-padding-lg; + } + + // Fix: Prevent unwanted hover styles from parent styles + // No text underline on hover + &:hover { + text-decoration: none; + } + + // Ensure title and description never have underline + .bds-card-offgrid__title, + .bds-card-offgrid__description { + text-decoration: none; + + &:hover { + text-decoration: none; + } + } + + // Ensure icon does not change color on hover + .bds-card-offgrid__icon-container { + // Icon color is inherited from card text color, which CardOffgrid manages + // No additional color changes should happen on hover + > * { + transition: none; + } + } + } +} + +// ============================================================================= +// DARK MODE (Default) - Section Text Colors +// ============================================================================= + +// Section text colors - Dark Mode (applies to both neutral and green card variants) +.bds-carousel-card-list--neutral, +.bds-carousel-card-list--green { + .bds-carousel-card-list__heading, + .bds-carousel-card-list__description { + color: $white; + } +} + +// ============================================================================= +// DARK MODE (Default) - Button Color Variants +// Button colors are independent of card colors - use buttonVariant prop +// ============================================================================= + +// Neutral button variant - Dark Mode +// Enabled: #CAD4DF (gray-300), Hover: #E6EAF0 (gray-200), Active: #CAD4DF (gray-300) +// Disabled: opacity 0.5 +.bds-carousel-card-list__button--neutral { + background-color: $gray-300; // #CAD4DF + color: $black; // #141414 + + &:hover:not(:disabled) { + background-color: $gray-200; // #E6EAF0 + } + + &:active:not(:disabled) { + background-color: $gray-300; // #CAD4DF (same as enabled) + } + + &.bds-carousel-card-list__button--disabled, + &:disabled { + background-color: $gray-300; // #CAD4DF + color: $gray-400; // #8A919A + opacity: 0.5; + cursor: not-allowed; + } +} + +// Green button variant - Dark Mode +// Enabled: #21E46B (green-300), Hover: #70EE97 (green-200), Active: #21E46B (green-300) +// Disabled: opacity 0.5 +.bds-carousel-card-list__button--green { + background-color: $green-300; // #21E46B + color: $black; // #141414 + + &:hover:not(:disabled) { + background-color: $green-200; // #70EE97 + } + + &:active:not(:disabled) { + background-color: $green-300; // #21E46B (same as enabled) + } + + &.bds-carousel-card-list__button--disabled, + &:disabled { + background-color: $green-300; // #21E46B + color: $gray-400; // #8A919A + opacity: 0.5; + cursor: not-allowed; + } +} + +// Black variant becomes White in Dark Mode (inverts for contrast) +// Enabled: #FFFFFF (white), Hover: #E6EAF0 (gray-200), Active: #FFFFFF (white) +// Disabled: opacity 0.5 +.bds-carousel-card-list__button--black { + background-color: $white; // #FFFFFF + color: $black; // #141414 + + &:hover:not(:disabled) { + background-color: $gray-200; // #E6EAF0 + } + + &:active:not(:disabled) { + background-color: $white; // #FFFFFF (same as enabled) + } + + &.bds-carousel-card-list__button--disabled, + &:disabled { + background-color: $white; // #FFFFFF + color: $gray-400; // #8A919A + opacity: 0.5; + cursor: not-allowed; + } +} + +// ============================================================================= +// LIGHT MODE (html.light) - Color Variants +// ============================================================================= + +html.light { + // Focus styles - Light Mode + .bds-carousel-card-list__button { + &:focus { + outline-color: $gray-900; + } + + &:focus-visible { + outline-color: $gray-900; + } + } + + .bds-carousel-card-list__track { + &:focus-visible { + outline-color: $gray-900; + } + } + + // Section text colors - Light Mode + .bds-carousel-card-list--neutral, + .bds-carousel-card-list--green { + .bds-carousel-card-list__heading, + .bds-carousel-card-list__description { + color: $black; + } + } + + // --------------------------------------------------------------------------- + // Button Color Variants - Light Mode + // Button colors are independent of card colors - use buttonVariant prop + // --------------------------------------------------------------------------- + + // Neutral button variant - Light Mode + // Enabled: #CAD4DF (gray-300), Hover: #E6EAF0 (gray-200), Active: #CAD4DF (gray-300) + // Disabled: opacity 0.5 + .bds-carousel-card-list__button--neutral { + background-color: $gray-300; // #CAD4DF + color: $black; // #141414 + + &:hover:not(:disabled) { + background-color: $gray-200; // #E6EAF0 + } + + &:active:not(:disabled) { + background-color: $gray-300; // #CAD4DF (same as enabled) + } + + &.bds-carousel-card-list__button--disabled, + &:disabled { + background-color: $gray-300; // #CAD4DF + color: $gray-400; // #8A919A + opacity: 0.5; + cursor: not-allowed; + } + } + + // Green button variant - Light Mode + // Enabled: #21E46B (green-300), Hover: #70EE97 (green-200), Active: #21E46B (green-300) + // Disabled: opacity 0.5 + .bds-carousel-card-list__button--green { + background-color: $green-300; // #21E46B + color: $black; // #141414 + + &:hover:not(:disabled) { + background-color: $green-200; // #70EE97 + } + + &:active:not(:disabled) { + background-color: $green-300; // #21E46B (same as enabled) + } + + &.bds-carousel-card-list__button--disabled, + &:disabled { + background-color: $green-300; // #21E46B + color: $gray-400; // #8A919A + opacity: 0.5; + cursor: not-allowed; + } + } + + // Black variant in Light Mode (stays black for contrast) + // Enabled: #141414 (black), Hover: #72777E (gray-500), Active: #141414 (black) + // Disabled: opacity 0.5 + .bds-carousel-card-list__button--black { + background-color: $black; // #141414 + color: $white; // #FFFFFF + + &:hover:not(:disabled) { + background-color: $gray-500; // #72777E + } + + &:active:not(:disabled) { + background-color: $black; // #141414 (same as enabled) + } + + &.bds-carousel-card-list__button--disabled, + &:disabled { + background-color: $black; // #141414 + color: $gray-400; // #8A919A + opacity: 0.5; + cursor: not-allowed; + } + } +} + diff --git a/shared/patterns/CarouselCardList/CarouselCardList.tsx b/shared/patterns/CarouselCardList/CarouselCardList.tsx new file mode 100644 index 000000000..6060dc494 --- /dev/null +++ b/shared/patterns/CarouselCardList/CarouselCardList.tsx @@ -0,0 +1,252 @@ +import React, { useRef, useState, useCallback, useEffect } from 'react'; +import clsx from 'clsx'; +import { CardOffgrid, CardOffgridProps } from '../../components/CardOffgrid'; + +/** + * Configuration for a single card in the CarouselCardList pattern + * Extends CardOffgridProps but removes variant (controlled by carousel) + */ +export type CarouselCardConfig = Omit; + +/** + * Props for the CarouselCardList pattern component + */ +export interface CarouselCardListProps extends React.ComponentPropsWithoutRef<'section'> { + /** Color variant of the cards */ + variant?: 'neutral' | 'green'; + /** Color variant of the navigation buttons (independent of card color). Defaults to 'neutral' */ + buttonVariant?: 'neutral' | 'green' | 'black'; + /** Section heading text */ + heading: React.ReactNode; + /** Section description text */ + description: React.ReactNode; + /** Array of card configurations */ + cards: readonly CarouselCardConfig[]; +} + +/** + * Generates a stable key for a card based on its properties. + */ +const getCardKey = (card: CarouselCardConfig, index: number): string | number => { + if (card.href) return card.href; + if (card.title) return `${card.title}-${index}`; + return index; +}; + +/** + * CarouselCardList Pattern Component + * + * A horizontal scrolling carousel that displays CardOffgrid components. + * Features navigation buttons that scroll cards in/out of view. + * The navigation button colors can be set independently of the card colors + * using the `buttonVariant` prop. + * + * @example + * ```tsx + * , title: "Feature 1", description: "..." }, + * { icon: , title: "Feature 2", description: "..." }, + * ]} + * /> + * ``` + */ +export const CarouselCardList = React.forwardRef( + (props, ref) => { + const { variant = 'neutral', buttonVariant = 'neutral', heading, description, cards, className, ...rest } = props; + + const scrollContainerRef = useRef(null); + const [canScrollPrev, setCanScrollPrev] = useState(false); + const [canScrollNext, setCanScrollNext] = useState(true); + + // Check scroll position and update button states + const updateScrollButtons = useCallback(() => { + const container = scrollContainerRef.current; + if (!container) return; + + const { scrollLeft, scrollWidth, clientWidth } = container; + setCanScrollPrev(scrollLeft > 0); + setCanScrollNext(scrollLeft + clientWidth < scrollWidth - 1); + }, []); + + // Initialize and listen for scroll events + useEffect(() => { + const container = scrollContainerRef.current; + if (!container) return; + + updateScrollButtons(); + container.addEventListener('scroll', updateScrollButtons, { passive: true }); + window.addEventListener('resize', updateScrollButtons); + + return () => { + container.removeEventListener('scroll', updateScrollButtons); + window.removeEventListener('resize', updateScrollButtons); + }; + }, [updateScrollButtons, cards.length]); + + // Scroll by one card width + const scroll = useCallback((direction: 'prev' | 'next') => { + const container = scrollContainerRef.current; + if (!container) return; + + // Get the first card to determine scroll amount + const card = container.querySelector('.bds-carousel-card-list__card') as HTMLElement; + if (!card) return; + + const cardWidth = card.offsetWidth; + const gap = 8; // 8px gap between cards + const scrollAmount = cardWidth + gap; + + container.scrollBy({ + left: direction === 'next' ? scrollAmount : -scrollAmount, + behavior: 'smooth', + }); + }, []); + + // Early return for empty cards + if (cards.length === 0) { + console.warn('CarouselCardList: No cards provided'); + return null; + } + + return ( +
+ {/* Header with title, description, and navigation buttons */} +
+
+

{heading}

+

{description}

+
+
+ scroll('prev')} + aria-label="Previous cards" + /> + scroll('next')} + aria-label="Next cards" + /> +
+
+ + {/* Cards scroll container - full bleed */} +
+
+ {cards.map((card, index) => ( +
+ +
+ ))} +
+
+
+ ); + } +); + +CarouselCardList.displayName = 'CarouselCardList'; + +/** + * Props for the CarouselButton component + */ +interface CarouselButtonProps extends React.ButtonHTMLAttributes { + /** Arrow direction */ + direction: 'prev' | 'next'; + /** Color variant (independent of card color) */ + variant: 'neutral' | 'green' | 'black'; +} + +/** + * Internal CarouselButton component for navigation + */ +const CarouselButton: React.FC = ({ + direction, + variant, + disabled, + className, + ...buttonProps +}) => { + return ( + + ); +}; + +/** + * SVG Arrow Icon for carousel navigation - Right arrow + */ +const CarouselArrowIconRight: React.FC = () => ( + +); + +/** + * SVG Arrow Icon for carousel navigation - Left arrow + */ +const CarouselArrowIconLeft: React.FC = () => ( + +); + +export default CarouselCardList; + diff --git a/shared/patterns/CarouselCardList/index.ts b/shared/patterns/CarouselCardList/index.ts new file mode 100644 index 000000000..234d0a914 --- /dev/null +++ b/shared/patterns/CarouselCardList/index.ts @@ -0,0 +1,3 @@ +export { CarouselCardList, type CarouselCardListProps, type CarouselCardConfig } from './CarouselCardList'; +export { default } from './CarouselCardList'; + diff --git a/static/css/devportal2024-v1.css b/static/css/devportal2024-v1.css index ed6b05726..f09e9f232 100644 --- a/static/css/devportal2024-v1.css +++ b/static/css/devportal2024-v1.css @@ -12666,262 +12666,6 @@ html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bd } } -.bds-btn--force-color.bds-btn--black.bds-btn--primary { - color: #FFFFFF !important; - background-color: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--primary::before { - background-color: rgba(20, 20, 20, 0.8) !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--primary .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--primary .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--primary .bds-btn__icon-chevron { - color: #FFFFFF !important; - stroke: #FFFFFF !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--primary:hover:not(:disabled):not(.bds-btn--disabled), .bds-btn--force-color.bds-btn--black.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled), .bds-btn--force-color.bds-btn--black.bds-btn--primary:active:not(:disabled):not(.bds-btn--disabled) { - color: #FFFFFF !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--primary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--primary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--primary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, .bds-btn--force-color.bds-btn--black.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, .bds-btn--force-color.bds-btn--black.bds-btn--primary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--primary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--primary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron { - color: #FFFFFF !important; - stroke: #FFFFFF !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--primary:focus-visible:not(:disabled):not(.bds-btn--disabled) { - outline-color: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--secondary { - color: #141414 !important; - border-color: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--secondary::before { - background-color: rgba(20, 20, 20, 0.15) !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--secondary .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled), .bds-btn--force-color.bds-btn--black.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled), .bds-btn--force-color.bds-btn--black.bds-btn--secondary:active:not(:disabled):not(.bds-btn--disabled) { - color: #141414 !important; - border-color: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, .bds-btn--force-color.bds-btn--black.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, .bds-btn--force-color.bds-btn--black.bds-btn--secondary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--secondary:focus-visible:not(:disabled):not(.bds-btn--disabled) { - outline-color: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary { - color: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled), .bds-btn--force-color.bds-btn--black.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled), .bds-btn--force-color.bds-btn--black.bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) { - color: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, .bds-btn--force-color.bds-btn--black.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, .bds-btn--force-color.bds-btn--black.bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -.bds-btn--force-color.bds-btn--black.bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled) { - outline-color: #141414 !important; -} - -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color { - color: #FFFFFF !important; - background-color: #141414 !important; -} -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color::before { - background-color: rgba(20, 20, 20, 0.8) !important; -} -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color .bds-btn__icon-chevron { - color: #FFFFFF !important; - stroke: #FFFFFF !important; -} -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled), html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled), html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) { - color: #FFFFFF !important; -} -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron { - color: #FFFFFF !important; - stroke: #FFFFFF !important; -} -html.dark .bds-btn.bds-btn.bds-btn--primary.bds-btn--primary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) { - outline-color: #FFFFFF !important; -} -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color { - color: #141414 !important; - border-color: #141414 !important; - background-color: transparent !important; -} -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color::before { - background-color: rgba(20, 20, 20, 0.15) !important; -} -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled), html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled), html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) { - color: #141414 !important; - border-color: #141414 !important; -} -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -html.dark .bds-btn.bds-btn.bds-btn--secondary.bds-btn--secondary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) { - outline-color: #FFFFFF !important; -} -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color { - color: #141414 !important; - background-color: transparent !important; -} -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled), html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled), html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) { - color: #141414 !important; -} -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:hover:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron, html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-line, -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:active:not(:disabled):not(.bds-btn--disabled) .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -html.dark .bds-btn.bds-btn.bds-btn--tertiary.bds-btn--tertiary.bds-btn--black.bds-btn--force-color:focus-visible:not(:disabled):not(.bds-btn--disabled) { - outline-color: #FFFFFF !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black { - color: #FFFFFF !important; - background-color: #141414 !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black .bds-btn__icon-chevron { - color: #FFFFFF !important; - stroke: #FFFFFF !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:hover, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus-visible, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:active, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:visited { - color: #FFFFFF !important; - background-color: #141414 !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:hover .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:hover .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:hover .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus-visible .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus-visible .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:focus-visible .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:active .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:active .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:active .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:visited .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:visited .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--primary.bds-btn--black:visited .bds-btn__icon-chevron { - color: #FFFFFF !important; - stroke: #FFFFFF !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black { - color: #141414 !important; - border-color: #141414 !important; - background-color: transparent !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:hover, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus-visible, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:active, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:visited { - color: #141414 !important; - border-color: #141414 !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:hover .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:hover .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:hover .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus-visible .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus-visible .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:focus-visible .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:active .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:active .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:active .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:visited .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:visited .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--secondary.bds-btn--black:visited .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black { - color: #141414 !important; - background-color: transparent !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:hover, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus-visible, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:active, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:visited { - color: #141414 !important; -} -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:hover .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:hover .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:hover .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus-visible .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus-visible .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:focus-visible .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:active .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:active .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:active .bds-btn__icon-chevron, html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:visited .bds-btn__icon, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:visited .bds-btn__icon-line, -html.dark a.bds-btn.bds-btn--force-color.bds-btn--tertiary.bds-btn--black:visited .bds-btn__icon-chevron { - color: #141414 !important; - stroke: #141414 !important; -} - .bds-btn--tertiary { color: #0DAA3E; background-color: transparent; @@ -13247,22 +12991,25 @@ html.dark .bds-btn--tertiary:not(.bds-btn--black):disabled, html.dark .bds-btn-- flex-direction: column; width: 100%; min-height: 200px; - height: 100%; - justify-content: space-between; - flex: 1; - padding: 8px; - gap: 4px; background-color: #C0A7FF; color: #141414; transition: transform 150ms cubic-bezier(0.98, 0.12, 0.12, 0.98); } +.bds-card-stat__content { + display: flex; + flex-direction: column; + justify-content: space-between; + flex: 1; + padding: 8px; + gap: 4px; +} @media (min-width: 576px) { - .bds-card-stat { + .bds-card-stat__content { padding: 12px; } } @media (min-width: 992px) { - .bds-card-stat { + .bds-card-stat__content { padding: 16px; } } @@ -13310,11 +13057,6 @@ html.dark .bds-btn--tertiary:not(.bds-btn--black):disabled, html.dark .bds-btn-- top: -0.4em; font-size: 0.7em; } -.bds-card-stat__statistic sup.bds-card-stat__superscript--numeric { - font-size: 0.5em; - top: -0.75em; - font-weight: 400; -} .bds-card-stat__buttons { display: flex; flex-wrap: wrap; @@ -13334,14 +13076,13 @@ html.dark .bds-btn--tertiary:not(.bds-btn--black):disabled, html.dark .bds-btn-- background-color: #E6EAF0; } -html.dark .bds-card-stat--light-gray { - background-color: #CAD4DF; -} - .bds-card-stat--dark-gray { background-color: #CAD4DF; } +html.dark .bds-card-stat--light-gray { + background-color: #CAD4DF; +} html.dark .bds-card-stat--dark-gray { background-color: #8A919A; } @@ -20197,402 +19938,324 @@ html.light .bds-callout-media-banner--image-text-black .bds-callout-media-banner color: #141414 !important; } -.bds-card-stats { - padding-top: 24px; - padding-bottom: 24px; -} -@media (min-width: 576px) { - .bds-card-stats { - padding-top: 32px; - padding-bottom: 32px; - } -} -@media (min-width: 992px) { - .bds-card-stats { - padding-top: 40px; - padding-bottom: 40px; - } -} - -.bds-card-stats__header { - display: flex; - flex-direction: column; - gap: 8px; - margin-bottom: 24px; -} -@media (min-width: 576px) { - .bds-card-stats__header { - margin-bottom: 32px; - } -} -@media (min-width: 992px) { - .bds-card-stats__header { - gap: 16px; - margin-bottom: 40px; - } -} - -html.dark .bds-card-stats__heading, -html.dark .bds-card-stats__description { - color: #FFFFFF; -} - -.bds-feature-two-column__button-group .bds-btn--tertiary { - padding-top: 0px !important; - padding-bottom: 0px !important; -} - -.bds-feature-two-column__cta-row .bds-btn--tertiary { - padding-top: 16px !important; -} - -.bds-feature-two-column { +.bds-carousel-card-list { width: 100%; } -@media (min-width: 992px) { - .bds-feature-two-column { +@media (min-width: 1280px) { + .bds-carousel-card-list { max-width: 1280px; - margin: 0 auto; + margin-left: auto; + margin-right: auto; } } -.bds-feature-two-column__desktop-layout { - display: none; -} -@media (min-width: 992px) { - .bds-feature-two-column__desktop-layout { - display: flex; - width: 100%; - align-items: stretch; - } -} -.bds-feature-two-column__mobile-layout { - display: block; -} -@media (min-width: 992px) { - .bds-feature-two-column__mobile-layout { - display: none; - } -} -.bds-feature-two-column__container { - padding: 0 !important; -} -.bds-feature-two-column__row { - gap: 0 !important; -} -.bds-feature-two-column__content-col { + +.bds-carousel-card-list__header { display: flex; flex-direction: column; - padding: 64px 0; -} -@media (min-width: 576px) { - .bds-feature-two-column__content-col { - padding: 80px 0; - } -} -@media (min-width: 992px) { - .bds-feature-two-column__content-col { - width: 50%; - padding: 96px 0; - aspect-ratio: 1/1; - } -} -.bds-feature-two-column__content-grid { - display: grid; - width: 100%; - height: 100%; - grid-template-columns: repeat(4, 1fr); - gap: 8px; - padding: 0 16px; -} -@media (min-width: 576px) { - .bds-feature-two-column__content-grid { - grid-template-columns: repeat(8, 1fr); - padding: 0 24px; - } -} -@media (min-width: 992px) { - .bds-feature-two-column__content-grid { - grid-template-columns: repeat(6, 1fr); - padding: 0 32px; - } -} -.bds-feature-two-column__content-wrapper { - grid-column: 1/-1; -} -@media (min-width: 576px) { - .bds-feature-two-column__content-wrapper { - grid-column: 2/span 6; - } -} -@media (min-width: 992px) { - .bds-feature-two-column__content-wrapper { - grid-column: 2/span 4; - } -} -.bds-feature-two-column__media-col { - display: flex; - flex-direction: column; -} -@media (min-width: 992px) { - .bds-feature-two-column__media-col { - width: 50%; - aspect-ratio: 1/1; - background-size: cover; - background-position: center; - background-repeat: no-repeat; - } -} -.bds-feature-two-column__media-col--mobile { - display: flex; - flex-direction: column; -} -.bds-feature-two-column__content { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: space-between; - height: 100%; gap: 24px; + padding-left: 18px; + padding-right: 18px; } @media (min-width: 576px) { - .bds-feature-two-column__content { + .bds-carousel-card-list__header { gap: 32px; + padding-left: 24px; + padding-right: 24px; } } @media (min-width: 992px) { - .bds-feature-two-column__content { - gap: 0; - } -} -.bds-feature-two-column__content--multiple { - gap: 0; - justify-content: flex-start; -} -@media (min-width: 992px) { - .bds-feature-two-column__content--multiple { - gap: 0; + .bds-carousel-card-list__header { + flex-direction: row; justify-content: space-between; + align-items: flex-start; + gap: 40px; + padding-left: 32px; + padding-right: 32px; } } -.bds-feature-two-column__content--multiple > .bds-btn--secondary { - align-self: flex-start; -} -.bds-feature-two-column__button-group { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 16px; - margin-top: 24px; -} -@media (min-width: 576px) { - .bds-feature-two-column__button-group { - gap: 16px; - margin-top: 32px; - } -} -@media (min-width: 992px) { - .bds-feature-two-column__button-group { - gap: 16px; - margin-top: 0; - } -} -.bds-feature-two-column__button-group > .bds-btn--tertiary { - padding-left: 0 !important; - margin-left: 0; -} -.bds-feature-two-column__button-group > .bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled), .bds-feature-two-column__button-group > .bds-btn--tertiary:focus:not(:disabled):not(.bds-btn--disabled), .bds-feature-two-column__button-group > .bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled), .bds-feature-two-column__button-group > .bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) { - padding-left: 0 !important; -} -.bds-feature-two-column__text-group { + +.bds-carousel-card-list__header-content { display: flex; flex-direction: column; gap: 8px; + max-width: 100%; } @media (min-width: 576px) { - .bds-feature-two-column__text-group { + .bds-carousel-card-list__header-content { gap: 8px; } } @media (min-width: 992px) { - .bds-feature-two-column__text-group { + .bds-carousel-card-list__header-content { gap: 16px; + max-width: 808px; } } -.bds-feature-two-column__title { - font-family: "Tobias", "Noto Serif", monospace; - font-weight: 300; - font-size: 32px; - line-height: 40px; - letter-spacing: 0px; - margin-bottom: 16px; -} -@media (min-width: 576px) { - .bds-feature-two-column__title { - font-size: 36px; - line-height: 45px; - letter-spacing: -0.5px; - margin-bottom: 16px; - } -} -@media (min-width: 992px) { - .bds-feature-two-column__title { - font-size: 40px; - line-height: 46px; - letter-spacing: -1px; - margin-bottom: 16px; - } -} -.bds-feature-two-column__title { - color: #141414; + +.bds-carousel-card-list__heading { margin: 0; } -.bds-feature-two-column__description { - font-family: "Booton", "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - font-weight: 300; - font-size: 16px; - line-height: 23.2px; - letter-spacing: 0px; - margin-bottom: 16px; -} -@media (min-width: 576px) { - .bds-feature-two-column__description { - font-size: 16px; - line-height: 23.2px; - letter-spacing: 0px; - margin-bottom: 16px; - } -} -@media (min-width: 992px) { - .bds-feature-two-column__description { - font-size: 18px; - line-height: 26.1px; - letter-spacing: -0.5px; - margin-bottom: 16px; - } -} -.bds-feature-two-column__description { - color: #141414; + +.bds-carousel-card-list__description { margin: 0; } -.bds-feature-two-column__cta { + +.bds-carousel-card-list__nav { display: flex; - flex-direction: column; - align-items: flex-start; + gap: 8px; + justify-content: flex-end; + flex-shrink: 0; + padding: 4px; + margin: -4px; } -@media (min-width: 576px) { - .bds-feature-two-column__cta { - flex-direction: row; - flex-wrap: wrap; + +.bds-carousel-card-list__button { + appearance: none; + border: none; + background: none; + padding: 0; + margin: 0; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 37px; + height: 37px; + transition: background-color 200ms cubic-bezier(0.98, 0.12, 0.12, 0.98), opacity 200ms cubic-bezier(0.98, 0.12, 0.12, 0.98); +} +@media (min-width: 992px) { + .bds-carousel-card-list__button { + width: 40px; + height: 40px; } } -.bds-feature-two-column__cta--single { - flex-direction: column; +.bds-carousel-card-list__button:focus { + outline: 2px solid #FFFFFF; + outline-offset: 2px; } -@media (min-width: 576px) { - .bds-feature-two-column__cta--double { - flex-direction: row; - align-items: center; +.bds-carousel-card-list__button:focus:not(:focus-visible) { + outline: none; +} +.bds-carousel-card-list__button:focus-visible { + outline: 2px solid #FFFFFF; + outline-offset: 2px; +} + +.bds-carousel-card-list__arrow-icon { + width: 18px; + height: 16px; +} +@media (max-width: 991.98px) { + .bds-carousel-card-list__arrow-icon { + width: 18px; + height: 15px; } } -.bds-feature-two-column__cta-row { - display: flex; - flex-direction: column; - align-items: flex-start; + +.bds-carousel-card-list__track-wrapper { + margin-top: 24px; + overflow: visible; + padding-left: 18px; } @media (min-width: 576px) { - .bds-feature-two-column__cta-row { - flex-direction: row; - align-items: center; + .bds-carousel-card-list__track-wrapper { + margin-top: 32px; + padding-left: 24px; } } @media (min-width: 992px) { - .bds-feature-two-column__cta-row { - flex-direction: row; - align-items: center; + .bds-carousel-card-list__track-wrapper { + margin-top: 40px; + padding-left: 32px; } } -.bds-feature-two-column__tertiary-group { + +.bds-carousel-card-list__track { display: flex; - flex-direction: column; - align-items: flex-start; - gap: 0; + gap: 8px; + overflow-x: auto; + overflow-y: visible; + scroll-snap-type: x mandatory; + scroll-behavior: smooth; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + -ms-overflow-style: none; } -.bds-feature-two-column__tertiary-group .bds-btn--tertiary { - padding-left: 0 !important; - margin-left: 0; +.bds-carousel-card-list__track::-webkit-scrollbar { + display: none; } -.bds-feature-two-column__tertiary-group .bds-btn--tertiary:hover:not(:disabled):not(.bds-btn--disabled), .bds-feature-two-column__tertiary-group .bds-btn--tertiary:focus:not(:disabled):not(.bds-btn--disabled), .bds-feature-two-column__tertiary-group .bds-btn--tertiary:focus-visible:not(:disabled):not(.bds-btn--disabled), .bds-feature-two-column__tertiary-group .bds-btn--tertiary:active:not(:disabled):not(.bds-btn--disabled) { - padding-left: 0 !important; +.bds-carousel-card-list__track { + padding-top: 4px; + padding-bottom: 4px; + margin-top: -4px; + margin-bottom: -4px; } -.bds-feature-two-column__media { - width: 100%; - overflow: hidden; - aspect-ratio: 1/1; +.bds-carousel-card-list__track:focus { + outline: none; +} +.bds-carousel-card-list__track:focus-visible { + outline: 2px solid #FFFFFF; + outline-offset: 4px; +} + +.bds-carousel-card-list__card { + flex-shrink: 0; + scroll-snap-align: start; +} +.bds-carousel-card-list__card .bds-card-offgrid { + width: 343px; + height: 400px; + padding: 16px; } @media (min-width: 576px) { - .bds-feature-two-column__media { - aspect-ratio: 16/9; + .bds-carousel-card-list__card .bds-card-offgrid { + width: 356px; + height: 440px; + padding: 20px; } } @media (min-width: 992px) { - .bds-feature-two-column__media { - aspect-ratio: 1/1; + .bds-carousel-card-list__card .bds-card-offgrid { + width: 400px; + height: 480px; + padding: 24px; } } -.bds-feature-two-column__media-img { - width: 100%; - height: 100%; - object-fit: cover; - object-position: center; +.bds-carousel-card-list__card .bds-card-offgrid:hover { + text-decoration: none; +} +.bds-carousel-card-list__card .bds-card-offgrid .bds-card-offgrid__title, +.bds-carousel-card-list__card .bds-card-offgrid .bds-card-offgrid__description { + text-decoration: none; +} +.bds-carousel-card-list__card .bds-card-offgrid .bds-card-offgrid__title:hover, +.bds-carousel-card-list__card .bds-card-offgrid .bds-card-offgrid__description:hover { + text-decoration: none; +} +.bds-carousel-card-list__card .bds-card-offgrid .bds-card-offgrid__icon-container > * { + transition: none; } -.bds-feature-two-column--neutral { - background-color: #F0F3F7; +.bds-carousel-card-list--neutral .bds-carousel-card-list__heading, +.bds-carousel-card-list--neutral .bds-carousel-card-list__description, +.bds-carousel-card-list--green .bds-carousel-card-list__heading, +.bds-carousel-card-list--green .bds-carousel-card-list__description { + color: #FFFFFF; } -.bds-feature-two-column--lilac { - background-color: #D9CAFF; -} - -.bds-feature-two-column--yellow { - background-color: #F3F1EB; -} - -.bds-feature-two-column--green { - background-color: #21E46B; -} - -html.dark .bds-feature-two-column__title { +.bds-carousel-card-list__button--neutral { + background-color: #CAD4DF; color: #141414; } -html.dark .bds-feature-two-column__description { - color: #141414; -} -html.dark .bds-feature-two-column--neutral { +.bds-carousel-card-list__button--neutral:hover:not(:disabled) { background-color: #E6EAF0; } -html.dark .bds-feature-two-column--lilac { - background-color: #D9CAFF; +.bds-carousel-card-list__button--neutral:active:not(:disabled) { + background-color: #CAD4DF; } -html.dark .bds-feature-two-column--yellow { - background-color: #F3F1EB; -} -html.dark .bds-feature-two-column--green { - background-color: #21E46B; +.bds-carousel-card-list__button--neutral.bds-carousel-card-list__button--disabled, .bds-carousel-card-list__button--neutral:disabled { + background-color: #CAD4DF; + color: #8A919A; + opacity: 0.5; + cursor: not-allowed; } -.bds-feature-two-column--right .bds-feature-two-column__row { - flex-direction: column-reverse !important; +.bds-carousel-card-list__button--green { + background-color: #21E46B; + color: #141414; } -@media (min-width: 992px) { - .bds-feature-two-column--right .bds-feature-two-column__desktop-layout { - flex-direction: row-reverse; - } +.bds-carousel-card-list__button--green:hover:not(:disabled) { + background-color: #70EE97; } -@media (min-width: 992px) { - .bds-feature-two-column--right .bds-feature-two-column__content-wrapper { - grid-column: 2/span 4; - } +.bds-carousel-card-list__button--green:active:not(:disabled) { + background-color: #21E46B; +} +.bds-carousel-card-list__button--green.bds-carousel-card-list__button--disabled, .bds-carousel-card-list__button--green:disabled { + background-color: #21E46B; + color: #8A919A; + opacity: 0.5; + cursor: not-allowed; +} + +.bds-carousel-card-list__button--black { + background-color: #FFFFFF; + color: #141414; +} +.bds-carousel-card-list__button--black:hover:not(:disabled) { + background-color: #E6EAF0; +} +.bds-carousel-card-list__button--black:active:not(:disabled) { + background-color: #FFFFFF; +} +.bds-carousel-card-list__button--black.bds-carousel-card-list__button--disabled, .bds-carousel-card-list__button--black:disabled { + background-color: #FFFFFF; + color: #8A919A; + opacity: 0.5; + cursor: not-allowed; +} + +html.light .bds-carousel-card-list__button:focus { + outline-color: #111112; +} +html.light .bds-carousel-card-list__button:focus-visible { + outline-color: #111112; +} +html.light .bds-carousel-card-list__track:focus-visible { + outline-color: #111112; +} +html.light .bds-carousel-card-list--neutral .bds-carousel-card-list__heading, +html.light .bds-carousel-card-list--neutral .bds-carousel-card-list__description, +html.light .bds-carousel-card-list--green .bds-carousel-card-list__heading, +html.light .bds-carousel-card-list--green .bds-carousel-card-list__description { + color: #141414; +} +html.light .bds-carousel-card-list__button--neutral { + background-color: #CAD4DF; + color: #141414; +} +html.light .bds-carousel-card-list__button--neutral:hover:not(:disabled) { + background-color: #E6EAF0; +} +html.light .bds-carousel-card-list__button--neutral:active:not(:disabled) { + background-color: #CAD4DF; +} +html.light .bds-carousel-card-list__button--neutral.bds-carousel-card-list__button--disabled, html.light .bds-carousel-card-list__button--neutral:disabled { + background-color: #CAD4DF; + color: #8A919A; + opacity: 0.5; + cursor: not-allowed; +} +html.light .bds-carousel-card-list__button--green { + background-color: #21E46B; + color: #141414; +} +html.light .bds-carousel-card-list__button--green:hover:not(:disabled) { + background-color: #70EE97; +} +html.light .bds-carousel-card-list__button--green:active:not(:disabled) { + background-color: #21E46B; +} +html.light .bds-carousel-card-list__button--green.bds-carousel-card-list__button--disabled, html.light .bds-carousel-card-list__button--green:disabled { + background-color: #21E46B; + color: #8A919A; + opacity: 0.5; + cursor: not-allowed; +} +html.light .bds-carousel-card-list__button--black { + background-color: #141414; + color: #FFFFFF; +} +html.light .bds-carousel-card-list__button--black:hover:not(:disabled) { + background-color: #72777E; +} +html.light .bds-carousel-card-list__button--black:active:not(:disabled) { + background-color: #141414; +} +html.light .bds-carousel-card-list__button--black.bds-carousel-card-list__button--disabled, html.light .bds-carousel-card-list__button--black:disabled { + background-color: #141414; + color: #8A919A; + opacity: 0.5; + cursor: not-allowed; } pre { @@ -26998,33 +26661,33 @@ html.light .progress { html.light [data-component-name="Search/SearchIcon"] > path { fill: black; } -html.light a:not(.bds-link), -html.light nav a:not(.bds-link), -html.light a:not([role=button]):not(.bds-link) { +html.light a:not(.bds-link):not(.bds-card-offgrid), +html.light nav a:not(.bds-link):not(.bds-card-offgrid), +html.light a:not([role=button]):not(.bds-link):not(.bds-card-offgrid) { color: #141414; } -html.light a:not(.bds-link).btn-primary, -html.light nav a:not(.bds-link).btn-primary, -html.light a:not([role=button]):not(.bds-link).btn-primary { +html.light a:not(.bds-link):not(.bds-card-offgrid).btn-primary, +html.light nav a:not(.bds-link):not(.bds-card-offgrid).btn-primary, +html.light a:not([role=button]):not(.bds-link):not(.bds-card-offgrid).btn-primary { color: #FFFFFF; } -html.light a:not(.bds-link).btn-primary:hover, -html.light nav a:not(.bds-link).btn-primary:hover, -html.light a:not([role=button]):not(.bds-link).btn-primary:hover { +html.light a:not(.bds-link):not(.bds-card-offgrid).btn-primary:hover, +html.light nav a:not(.bds-link):not(.bds-card-offgrid).btn-primary:hover, +html.light a:not([role=button]):not(.bds-link):not(.bds-card-offgrid).btn-primary:hover { color: #FFFFFF; } -html.light a:not(.bds-link):hover, html.light a:not(.bds-link):active, html.light a:not(.bds-link).active, -html.light nav a:not(.bds-link):hover, -html.light nav a:not(.bds-link):active, -html.light nav a:not(.bds-link).active, -html.light a:not([role=button]):not(.bds-link):hover, -html.light a:not([role=button]):not(.bds-link):active, -html.light a:not([role=button]):not(.bds-link).active { +html.light a:not(.bds-link):not(.bds-card-offgrid):hover, html.light a:not(.bds-link):not(.bds-card-offgrid):active, html.light a:not(.bds-link):not(.bds-card-offgrid).active, +html.light nav a:not(.bds-link):not(.bds-card-offgrid):hover, +html.light nav a:not(.bds-link):not(.bds-card-offgrid):active, +html.light nav a:not(.bds-link):not(.bds-card-offgrid).active, +html.light a:not([role=button]):not(.bds-link):not(.bds-card-offgrid):hover, +html.light a:not([role=button]):not(.bds-link):not(.bds-card-offgrid):active, +html.light a:not([role=button]):not(.bds-link):not(.bds-card-offgrid).active { color: #5429A1; } -html.light a:not(.bds-link):not(.btn):focus, -html.light nav a:not(.bds-link):not(.btn):focus, -html.light a:not([role=button]):not(.bds-link):not(.btn):focus { +html.light a:not(.bds-link):not(.bds-card-offgrid):not(.btn):focus, +html.light nav a:not(.bds-link):not(.bds-card-offgrid):not(.btn):focus, +html.light a:not([role=button]):not(.bds-link):not(.bds-card-offgrid):not(.btn):focus { background-color: transparent; } html.light a.card:hover, html.light:active, html.light.active { diff --git a/styles/light/_light-theme.scss b/styles/light/_light-theme.scss index ca321f119..378a6fabf 100644 --- a/styles/light/_light-theme.scss +++ b/styles/light/_light-theme.scss @@ -123,9 +123,9 @@ h6, // Navigation ------------------------------------------------------------------ -a:not(.bds-link), -nav a:not(.bds-link), -a:not([role="button"]):not(.bds-link) { +a:not(.bds-link):not(.bds-card-offgrid), +nav a:not(.bds-link):not(.bds-card-offgrid), +a:not([role="button"]):not(.bds-link):not(.bds-card-offgrid) { color: $light-fg; &.btn-primary { diff --git a/styles/xrpl.scss b/styles/xrpl.scss index e470158de..3371e562f 100644 --- a/styles/xrpl.scss +++ b/styles/xrpl.scss @@ -103,6 +103,7 @@ $line-height-base: 1.5; @import "../shared/patterns/CalloutMediaBanner/CalloutMediaBanner.scss"; @import "../shared/patterns/CardStats/CardStats.scss"; @import "../shared/patterns/FeatureTwoColumn/FeatureTwoColumn.scss"; +@import "../shared/patterns/CarouselCardList/CarouselCardList.scss"; @import "_code-tabs.scss"; @import "_code-walkthrough.scss"; @import "_diagrams.scss";