add new PanelStack component to replace CarouselFeatured component

This commit is contained in:
Alexander Mahan
2026-08-18 14:26:39 -07:00
parent f663b6f787
commit 2d60c3d4a2
7 changed files with 203 additions and 9 deletions

View File

@@ -3,9 +3,9 @@ import { useThemeHooks } from "@redocly/theme/core/hooks";
import { PageWrapper } from "shared/components/PageWrapper";
import { HeaderHeroSplitMedia } from "shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia";
import {
CarouselFeatured,
type CarouselSlide,
} from "shared/patterns/CarouselFeatured/CarouselFeatured";
PanelStack,
type PanelStackPanel,
} from "shared/patterns/PanelStack";
import { LogoSquareGrid } from "shared/sections/LogoSquareGrid";
import { LinkTextDirectory } from "shared/sections/LinkTextDirectory/LinkTextDirectory";
import { FeatureSingleTopic } from "shared/sections/FeatureSingleTopic/FeatureSingleTopic";
@@ -22,7 +22,7 @@ export default function Funding() {
const { useTranslate } = useThemeHooks();
const { translate } = useTranslate();
const carouselSlides: CarouselSlide[] = [
const carouselSlides: PanelStackPanel[] = [
{
id: "xrpl-grants-accelerator",
heading: translate("Explore XRPL Funding and Builders Programs"),
@@ -156,7 +156,7 @@ export default function Funding() {
}}
/>
<CarouselFeatured slides={carouselSlides} background="yellow" />
<PanelStack slides={carouselSlides} background="yellow" />
<LogoSquareGrid
variant="gray"

View File

@@ -4,7 +4,7 @@ import { PageWrapper } from 'shared/components/PageWrapper';
import { HeaderHeroSplitMedia } from 'shared/sections/HeaderHeroSplitMedia/HeaderHeroSplitMedia';
import { CarouselCardList } from 'shared/sections/CarouselCardList/CarouselCardList';
import { LogoRectangleGrid } from 'shared/sections/LogoRectangleGrid/LogoRectangleGrid';
import { CarouselFeatured } from 'shared/patterns/CarouselFeatured/CarouselFeatured';
import { PanelStack } from 'shared/patterns/PanelStack';
import { SmallTilesSection } from 'shared/sections/SmallTilesSection/SmallTilesSection';
import { LinkTextDirectory } from 'shared/sections/LinkTextDirectory/LinkTextDirectory';
import { FeatureTwoColumn } from 'shared/sections/FeatureTwoColumn/FeatureTwoColumn';
@@ -116,7 +116,7 @@ export default function Tokenization() {
/>
{/* 5. Token Utility and Market Integration — Carousel Featured (4 slides from Assets frame) */}
<CarouselFeatured
<PanelStack
background="neutral"
slides={[
{

View File

@@ -13,7 +13,7 @@ import { FeatureSingleTopic } from "shared/sections/FeatureSingleTopic";
import type { StandardCardPropsWithoutVariant } from "shared/components/StandardCard";
import { BdsLink } from "shared/components/Link";
import { HomeHero } from "shared/sections/HomeHero/HomeHero";
import { CarouselFeatured } from "shared/patterns/CarouselFeatured";
import { PanelStack } from "shared/patterns/PanelStack";
export const frontmatter = {
seo: {
@@ -231,7 +231,7 @@ export default function Index() {
cards={blockchainCardStats}
/>
<CarouselFeatured
<PanelStack
slides={[
{
id: 0,

View File

@@ -0,0 +1,28 @@
// BDS PanelStack Pattern Styles
// Brand Design System - Vertically stacked featured panels
//
// Reuses CarouselFeatured slide styles (`.bds-carousel-featured*`) so each
// panel matches a carousel slide. This file only handles the vertical stack.
//
// Naming Convention: BEM with 'bds' namespace
// .bds-panel-stack - Outer stack (document flow, no carousel track)
// .bds-panel-stack__panel - Individual panel (also has carousel-featured classes)
.bds-panel-stack {
display: flex;
flex-direction: column;
width: 100%;
gap: $bds-space-2xl;
@include media-breakpoint-up(md) {
gap: $bds-space-3xl;
}
@include media-breakpoint-up(lg) {
gap: $bds-space-4xl;
}
}
.bds-panel-stack__panel {
overflow: hidden;
}

View File

@@ -0,0 +1,158 @@
import React from "react";
import clsx from "clsx";
import { Divider } from "../../components/Divider";
import { PageGrid, PageGridRow, PageGridCol } from "../../components/PageGrid";
import { ButtonGroup } from "../ButtonGroup/ButtonGroup";
import type {
CarouselFeaturedBackground,
CarouselFeaturedProps,
CarouselSlide,
} from "../CarouselFeatured/CarouselFeatured";
/**
* Props for a single panel in the PanelStack component.
* Same shape as a CarouselFeatured slide.
*/
export type PanelStackPanel = CarouselSlide;
/**
* Background color options for PanelStack.
* Same variants as CarouselFeatured; each adapts to light/dark mode.
*/
export type PanelStackBackground = CarouselFeaturedBackground;
/**
* Props for the PanelStack pattern component.
* Matches CarouselFeatured, minus carousel-only behavior.
*/
export interface PanelStackProps extends CarouselFeaturedProps {
/** Array of panels to display. Same shape as CarouselFeatured slides. */
slides: readonly CarouselSlide[];
/** Background color variant. Defaults to 'grey'. */
background?: PanelStackBackground;
}
/**
* PanelStack Pattern Component
*
* Statically stacks featured panels vertically using the same two-column
* slide layout as CarouselFeatured (image left / content right on desktop;
* content top / image bottom on tablet and mobile). There is no carousel
* track, navigation, or other interaction.
*
* @example
* ```tsx
* <PanelStack
* slides={[
* {
* id: 1,
* heading: "Powered by Developers",
* features: [
* { title: "Easy-to-Integrate APIs", description: "Build with common languages..." },
* { title: "Full Lifecycle Support", description: "From dev tools to deployment..." },
* ],
* buttons: [
* { label: "Get Started", href: "/docs" },
* { label: "Learn More", href: "/about" }
* ],
* imageSrc: '/image1.jpg',
* imageAlt: 'Panel 1'
* },
* ]}
* />
* ```
*/
export const PanelStack = React.forwardRef<HTMLElement, PanelStackProps>(
(props, ref) => {
const { slides, background = "grey", className, children, ...rest } = props;
if (slides.length === 0) {
console.warn("PanelStack: No slides provided");
return null;
}
return (
<section
ref={ref}
className={clsx("bds-panel-stack", className)}
{...rest}
>
{slides.map((slide, index) => (
<PageGrid
key={slide.id}
className={clsx(
"bds-carousel-featured",
`bds-carousel-featured--bg-${background}`,
"bds-panel-stack__panel",
)}
>
<PageGridRow>
<PageGridCol
span={{ base: 4, md: 8, lg: 6 }}
className="bds-carousel-featured__content-col order-1 order-lg-2"
>
<div className="bds-carousel-featured__content">
<div className="bds-carousel-featured__header">
<h2 className="bds-carousel-featured__heading h-md">
{slide.heading}
</h2>
</div>
<div className="bds-carousel-featured__bottom">
<ul className="bds-carousel-featured__features">
{slide.features.map((feature, featureIndex) => (
<li
key={featureIndex}
className="bds-carousel-featured__feature"
>
<Divider color="base" weight="regular" />
<div className="bds-carousel-featured__feature-title body-r">
{feature.title}
</div>
<div className="bds-carousel-featured__feature-description label-l">
{feature.description}
</div>
</li>
))}
</ul>
<div className="bds-carousel-featured__cta">
{slide.buttons && slide.buttons.length > 0 && (
<ButtonGroup
buttons={slide.buttons}
maxButtons={2}
color="black"
forceColor={background !== "neutral"}
className="bds-carousel-featured__buttons"
/>
)}
</div>
</div>
</div>
</PageGridCol>
<PageGridCol
span={{ base: 4, md: 8, lg: 6 }}
className="bds-carousel-featured__media-col order-2 order-lg-1"
>
<div className="bds-carousel-featured__media">
<img
src={slide.imageSrc}
alt={slide.imageAlt}
className="bds-carousel-featured__image"
loading={index === 0 ? "eager" : "lazy"}
/>
</div>
</PageGridCol>
</PageGridRow>
</PageGrid>
))}
{children}
</section>
);
},
);
PanelStack.displayName = "PanelStack";
export default PanelStack;

View File

@@ -0,0 +1,7 @@
export {
PanelStack,
type PanelStackProps,
type PanelStackBackground,
type PanelStackPanel,
} from "./PanelStack";
export { default } from "./PanelStack";

View File

@@ -113,6 +113,7 @@ $line-height-base: 1.5;
@import "../shared/sections/CardStatsList/CardStatsList.scss";
@import "../shared/sections/CarouselCardList/CarouselCardList.scss";
@import "../shared/patterns/CarouselFeatured/CarouselFeatured.scss";
@import "../shared/patterns/PanelStack/PanelStack.scss";
@import "../shared/sections/LogoSquareGrid/LogoSquareGrid.scss";
@import "../shared/patterns/SectionHeader/SectionHeader.scss";
@import "../shared/sections/CardsFeatured/CardsFeatured.scss";