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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Built CSS regenerated with the production script.

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

---------

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

9.5 KiB

FeatureTwoColumn Pattern

A feature section pattern that pairs editorial content with a media element in a two-column layout. Designed for showcasing features, products, or use cases with a uniform tertiary link treatment.

Overview

FeatureTwoColumn supports four color theme variants (neutral, lilac, yellow, green) and adapts responsively across desktop, tablet, and mobile breakpoints. Links always render as tertiary buttons; only their layout adjusts to the number provided.

Layout is implemented as a single flex structure (no separate mobile/desktop markup): below the desktop breakpoint, media always appears above content so stacked sections read consistently as media → content → media → content. The arrange prop only affects desktop (≥992px), where it alternates content left vs. content right.

When to Use

  • Highlighting a specific feature, product, or use case
  • Presenting content with supporting visual media
  • Creating visual variety with alternating left/right arrangements on desktop
  • When 1-5 action links are needed as a uniform set of text links (no primary/secondary hierarchy)

Props

Prop Type Default Description
color 'neutral' | 'lilac' | 'yellow' | 'green' 'neutral' Background color theme variant
arrange 'left' | 'right' 'left' Desktop only: content column on the left (left) or right (right). Below desktop, layout is always media on top, then content.
title string required Feature title text (heading-md typography)
description string required Feature description text (body-l typography)
links FeatureTwoColumnLink[] required Array of 1-5 links, all rendered as tertiary buttons (see button behavior below)
media { src: string; alt: string } required Feature media (image) configuration
className string - Additional CSS classes
Property Type Description
label string Link label text
href string Link URL

Button Behavior

The component renders a ButtonGroup from the links you pass.

Every link renders as a tertiary button, regardless of count. This section pairs its links with editorial copy, so the design calls for one uniform text-link treatment rather than ButtonGroup's default count-based mix. It's implemented by passing forceVariant="tertiary" and forceNoPadding, so the buttons also sit flush with the title and description instead of being indented by the button's horizontal padding.

Only the layout still varies with the count:

Link Count Layout
1-2 links Inline (stacks on mobile, row at md+), 16px gap / 24px at lg+
3-5 links Vertical block layout on all screen sizes (per ButtonGroup rules)

Variants

Color Themes

Background tokens are defined in FeatureTwoColumn.scss ($bds-feature-variants). At a glance:

  • Neutral: Light neutral background (dark mode uses a slightly darker neutral)
  • Lilac, Yellow, Green: Theme-colored backgrounds (light/dark pairs in SCSS)

Arrangement (arrange)

  • left (default, desktop): Content column on the left, media on the right (50% / 50% row).
  • right (desktop): Media on the left, content on the right (flex-direction: row-reverse at ≥992px).
  • Mobile and tablet: arrange does not change stacking; media is always on top, then content, so repeated sections keep the same vertical rhythm.

Basic Usage

import { FeatureTwoColumn } from 'shared/sections/FeatureTwoColumn';

function MyPage() {
  return (
    <FeatureTwoColumn
      color="lilac"
      arrange="left"
      title="Institutions"
      description="Banks, asset managers, PSPs, and fintechs use XRPL to build financial products and DeFi solutions efficiently and with more flexibility."
      links={[
        { label: "Get Started", href: "/docs" },
        { label: "Learn More", href: "/about" }
      ]}
      media={{ src: "/img/institutions.png", alt: "Institutions illustration" }}
    />
  );
}

Examples

<FeatureTwoColumn
  color="green"
  arrange="right"
  title="Developers"
  description="Build powerful applications on XRPL with comprehensive documentation and tools."
  links={[{ label: "View Documentation", href: "/docs" }]}
  media={{ src: "/img/dev.png", alt: "Developer tools" }}
/>
<FeatureTwoColumn
  color="yellow"
  arrange="left"
  title="Enterprise Solutions"
  description="Scale your business with blockchain technology."
  links={[
    { label: "Contact Sales", href: "/contact" },
    { label: "Learn More", href: "/enterprise" }
  ]}
  media={{ src: "/img/enterprise.png", alt: "Enterprise" }}
/>
<FeatureTwoColumn
  color="neutral"
  arrange="left"
  title="Explore XRPL"
  description="Discover all the ways to interact with the XRP Ledger."
  links={[
    { label: "Documentation", href: "/docs" },
    { label: "Tutorials", href: "/tutorials" },
    { label: "API Reference", href: "/api" },
    { label: "Community", href: "/community" },
    { label: "GitHub", href: "/github" }
  ]}
  media={{ src: "/img/explore.png", alt: "Explore XRPL" }}
/>

Responsive Behavior

Implementation details live in FeatureTwoColumn.scss. Summary:

Desktop (≥992px)

  • Layout: One row, two equal-width columns (width: 50% each), square (1:1) media column.
  • arrange: left = content | media; right = media | content.
  • Content column: Internal CSS grid (6 tracks) offsets the text block; vertical padding 96px.
  • Media: <img> with object-fit: cover filling the media column (no separate background-image layout).

Tablet (576px - 991px)

  • Layout: Stacked column; media on top, content below (flex-direction: column-reverse on the layout wrapper).
  • Media: 16:9 aspect ratio.
  • Content column: Vertical padding 80px; inner grid uses 8 columns with inset content span.

Mobile (<576px)

  • Layout: Same stacking as tablet — media on top, then content.
  • Media: 1:1 aspect ratio.
  • Content column: Vertical padding 64px; inner grid uses 4 columns (full-width content span).

Anatomy

FeatureTwoColumn (<section>)
└── .bds-feature-two-column__layout (flex: column-reverse < lg; row ≥ lg; row-reverse ≥ lg when arrange=right)
    ├── .bds-feature-two-column__content-col
    │   └── .bds-feature-two-column__content-grid (responsive column grid + horizontal padding)
    │       └── .bds-feature-two-column__content-wrapper (grid placement)
    │           └── .bds-feature-two-column__content [+ .bds-feature-two-column__content--multiple when 3+ links]
    │               ├── .bds-feature-two-column__text-group
    │               │   ├── .bds-feature-two-column__title (<h2>, heading-md)
    │               │   └── .bds-feature-two-column__description (<p>, body-l)
    │               └── ButtonGroup (when links present)
    └── .bds-feature-two-column__media-col
        └── .bds-feature-two-column__media
            └── .bds-feature-two-column__media-img (<img>)

CSS Classes

Class Description
.bds-feature-two-column Root <section>
.bds-feature-two-column--neutral Neutral color theme
.bds-feature-two-column--lilac Lilac color theme
.bds-feature-two-column--yellow Yellow color theme
.bds-feature-two-column--green Green color theme
.bds-feature-two-column--left Desktop: content left (default row order)
.bds-feature-two-column--right Desktop: content right (row-reverse at ≥992px)
.bds-feature-two-column__layout Flex wrapper for content + media columns
.bds-feature-two-column__content-col Content column (padding, half width on desktop)
.bds-feature-two-column__content-grid Inner grid for horizontal alignment of copy
.bds-feature-two-column__content-wrapper Grid cell spanning the intended columns
.bds-feature-two-column__content Flex column for text group + buttons
.bds-feature-two-column__content--multiple Modifier when three or more links (adjusted flex gaps)
.bds-feature-two-column__text-group Title + description container
.bds-feature-two-column__title Title heading
.bds-feature-two-column__description Description text
.bds-feature-two-column__media-col Media column wrapper
.bds-feature-two-column__media Media aspect-ratio box
.bds-feature-two-column__media-img Image element

Accessibility

  • Uses semantic <section> for the pattern container.
  • Title uses <h2> for document outline.
  • Media uses a real <img> with required alt text.
  • Buttons inherit accessible behavior from the Button / ButtonGroup components.
  • Note: On viewports below desktop, visual order is media then content, but DOM order remains content subtree first, then media. Keyboard and screen-reader order follow the DOM; if reading order must match the visual stack, consider page-level structure or future enhancements.

Design References

  • Figma Design: Pattern - Feature - Two Column
  • Component Location: shared/sections/FeatureTwoColumn/
  • Color Tokens: styles/_colors.scss
  • Typography: styles/_font.scss