Files
xrpl-dev-portal/shared/components/PageGrid/page-grid.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

8.8 KiB

PageGrid Component

A responsive grid system component for creating flexible layouts with support for multiple breakpoints.

Overview

The PageGrid component provides a flexible grid layout system with three main components:

  • PageGrid - The container component
  • PageGrid.Row - Row component for grouping columns
  • PageGrid.Col - Column component with responsive sizing and offset support

Breakpoints

The grid system supports the following breakpoints:

  • base - Default/mobile (applies to all sizes)
  • sm - Small screens
  • md - Medium screens
  • lg - Large screens
  • xl - Extra large screens

Basic Usage

import { PageGrid } from '@/shared/components/PageGrid/page-grid';

function MyComponent() {
  return (
    <PageGrid>
      <PageGrid.Row>
        <PageGrid.Col span={6}>
          Column 1
        </PageGrid.Col>
        <PageGrid.Col span={6}>
          Column 2
        </PageGrid.Col>
      </PageGrid.Row>
    </PageGrid>
  );
}

PageGrid Props

The root PageGrid component accepts all standard HTML div attributes:

Prop Type Default Description
containerType "standard" | "wide" "standard" Container layout type. See Container types
as React.ElementType "div" Polymorphic element
className string - Additional CSS classes to apply
...rest HTMLDivElement attributes - Any other HTML div attributes

Container types

Value Behavior
"standard" (default) The standard container width and padding.
"wide" Adds bds-grid__container--wide: no horizontal padding below xl, 80px at xl, then 112px padding with a 1504px max-width at xxl. Use for full-bleed-ish sections that should still align to the grid.
<PageGrid containerType="wide">
  <PageGrid.Row>
    <PageGrid.Col span={12}>Wide section</PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

PageGrid.Row Props

The PageGrid.Row component accepts all standard HTML div attributes:

Prop Type Default Description
as React.ElementType "div" Polymorphic element — e.g. "ul" for semantic list markup
className string - Additional CSS classes to apply
...rest HTMLDivElement attributes - Any other HTML div attributes

PageGrid.Col Props

Prop Type Default Description
as React.ElementType "div" Polymorphic element — e.g. "li" for semantic list markup
span number | "auto" | "fill" | ResponsiveValue - Column span width
offset number | ResponsiveValue - Column offset (left margin)
className string - Additional CSS classes to apply
...rest HTMLDivElement attributes - Any other HTML div attributes

Semantic lists with as

When a grid of items is genuinely a list, render the row as ul and the columns as li so the markup carries the right semantics:

<PageGrid.Row as="ul">
  <PageGrid.Col as="li" span={{ base: 4, lg: 6 }}>Item one</PageGrid.Col>
  <PageGrid.Col as="li" span={{ base: 4, lg: 6 }}>Item two</PageGrid.Col>
</PageGrid.Row>

_page-grid.scss includes an li.bds-grid__col rule that strips list-item defaults so flex layout and width calculations still apply. This is the pattern CardsTextGrid and CardsIconGrid use, via CardTextIconCard's gridColSpan prop.

Span Values

  • Number (e.g., 1-12): Fixed column width
  • "auto": Column takes up only the space it needs
  • "fill": Column fills remaining available space
  • Responsive Object: Different spans for different breakpoints

Offset Values

  • Number (e.g., 1-12): Number of columns to offset
  • Responsive Object: Different offsets for different breakpoints

Examples

Fixed Column Widths

<PageGrid>
  <PageGrid.Row>
    <PageGrid.Col span={4}>Sidebar</PageGrid.Col>
    <PageGrid.Col span={8}>Main Content</PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

Auto and Fill

<PageGrid>
  <PageGrid.Row>
    <PageGrid.Col span="auto">Minimal Width</PageGrid.Col>
    <PageGrid.Col span="fill">Takes Remaining Space</PageGrid.Col>
    <PageGrid.Col span="auto">Another Auto</PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

Responsive Layout

Create layouts that adapt to different screen sizes:

<PageGrid>
  <PageGrid.Row>
    <PageGrid.Col 
      span={{
        base: 12,    // Full width on mobile
        sm: 12,      // Full width on small screens
        md: 6,       // Half width on medium screens
        lg: 4,       // Third width on large screens
        xl: 3        // Quarter width on extra large screens
      }}
    >
      Responsive Column
    </PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

Using Offsets

Center content or create spacing with offsets:

<PageGrid>
  <PageGrid.Row>
    {/* Center an 8-column element */}
    <PageGrid.Col span={8} offset={2}>
      Centered Content
    </PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

Responsive Offsets

<PageGrid>
  <PageGrid.Row>
    <PageGrid.Col 
      span={6}
      offset={{
        base: 0,     // No offset on mobile
        md: 3        // Offset by 3 columns on medium+ screens
      }}
    >
      Content
    </PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

Complex Responsive Layout

<PageGrid>
  <PageGrid.Row>
    {/* Hero section */}
    <PageGrid.Col span={12}>
      <h1>Page Title</h1>
    </PageGrid.Col>
  </PageGrid.Row>
  
  <PageGrid.Row>
    {/* Sidebar - full width on mobile, 1/3 on desktop */}
    <PageGrid.Col 
      span={{
        base: 12,
        lg: 4
      }}
    >
      <aside>Sidebar</aside>
    </PageGrid.Col>
    
    {/* Main content - full width on mobile, 2/3 on desktop */}
    <PageGrid.Col 
      span={{
        base: 12,
        lg: 8
      }}
    >
      <main>Main Content</main>
    </PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

Multiple Columns with Equal Width

<PageGrid>
  <PageGrid.Row>
    <PageGrid.Col span={3}>Column 1</PageGrid.Col>
    <PageGrid.Col span={3}>Column 2</PageGrid.Col>
    <PageGrid.Col span={3}>Column 3</PageGrid.Col>
    <PageGrid.Col span={3}>Column 4</PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

Nested Grids

<PageGrid>
  <PageGrid.Row>
    <PageGrid.Col span={12}>
      <PageGrid>
        <PageGrid.Row>
          <PageGrid.Col span={6}>Nested Column 1</PageGrid.Col>
          <PageGrid.Col span={6}>Nested Column 2</PageGrid.Col>
        </PageGrid.Row>
      </PageGrid>
    </PageGrid.Col>
  </PageGrid.Row>
</PageGrid>

CSS Classes Generated

The component generates the following CSS classes:

Container

  • bds-grid__container
  • bds-grid__container--wide (when containerType="wide")

Row

  • bds-grid__row

Column Spans

  • bds-grid__col-{number} (e.g., bds-grid__col-6)
  • bds-grid__col-auto
  • bds-grid__col (for fill)
  • bds-grid__col-{breakpoint}-{number} (e.g., bds-grid__col-md-6)
  • bds-grid__col-{breakpoint}-auto
  • bds-grid__col-{breakpoint} (for fill)

Column Offsets

  • bds-grid__offset-{number} (e.g., bds-grid__offset-2)
  • bds-grid__offset-{breakpoint}-{number} (e.g., bds-grid__offset-md-2)

TypeScript Types

type PageGridBreakpoint = "base" | "sm" | "md" | "lg" | "xl";
type ResponsiveValue<T> = T | Partial<Record<PageGridBreakpoint, T>>;
type PageGridSpanValue = number | "auto" | "fill";
type PageGridOffsetValue = number;

interface PageGridProps {
  containerType?: "standard" | "wide";
  as?: React.ElementType;
  className?: string;
  // ... plus all HTMLDivElement attributes
}

interface PageGridRowProps {
  as?: React.ElementType;
  className?: string;
  // ... plus all HTMLDivElement attributes
}

interface PageGridColProps {
  as?: React.ElementType;
  span?: ResponsiveValue<PageGridSpanValue>;
  offset?: ResponsiveValue<PageGridOffsetValue>;
  className?: string;
  // ... plus all HTMLDivElement attributes
}

Best Practices

  1. Use semantic HTML: Wrap content in appropriate semantic elements within columns
  2. Mobile-first: Start with base/mobile layout and progressively enhance for larger screens
  3. Keep it simple: Avoid overly complex nested grid structures when possible
  4. Test responsiveness: Verify layouts work well at all breakpoint transitions
  5. Accessibility: Ensure grid layouts maintain logical reading order for screen readers

Notes

  • The total columns in the grid system is typically 12 (though this depends on your CSS implementation)
  • Responsive values cascade - if you don't specify a value for a breakpoint, it inherits from the previous breakpoint
  • All components forward refs, allowing you to attach refs to the underlying DOM elements