* 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>
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 componentPageGrid.Row- Row component for grouping columnsPageGrid.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 screensmd- Medium screenslg- Large screensxl- 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__containerbds-grid__container--wide(whencontainerType="wide")
Row
bds-grid__row
Column Spans
bds-grid__col-{number}(e.g.,bds-grid__col-6)bds-grid__col-autobds-grid__col(for fill)bds-grid__col-{breakpoint}-{number}(e.g.,bds-grid__col-md-6)bds-grid__col-{breakpoint}-autobds-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
- Use semantic HTML: Wrap content in appropriate semantic elements within columns
- Mobile-first: Start with base/mobile layout and progressively enhance for larger screens
- Keep it simple: Avoid overly complex nested grid structures when possible
- Test responsiveness: Verify layouts work well at all breakpoint transitions
- 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