Card
The Card component contains content and actions that inform about a single subject.
Default Card
The default card view with a rectangle shaped image, a title, a description and a button for some additional actions.
This block contain empty anchor element, this specific manipulation adds possibility to navigate with tab through whole card. With this structure we can click buttons inside or whole card itself. If root card element would be anchor element, we would not have possibility to click button inside.
import { SfButton } from '@storefront-ui/react';
const cardDetails = [
{
image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/card-3.png',
title: "Trail Running: Nature's Ultimate Challenge",
description:
"Explore the exhilarating world of trail running. Embrace nature's rugged terrains, elevate your fitness, and learn to conquer every path.",
button: 'Read more',
},
{
image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/card-2.png',
title: 'Ride the Future: Exploring the Benefits of e-Bikes',
description:
'Eco-friendly, efficient, and fun modes of transportation that provide a range of benefits for riders and the environment.',
button: 'Read more',
},
{
image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/card-1.png',
title: 'Unleash the Ultimate Listening Experience',
description:
'Audiophile headphones offer unmatched sound quality and clarity, making them the go-to choice for music enthusiasts.',
button: 'Read more',
},
];
export default function CardDefault() {
return (
<div className="flex flex-wrap gap-4 lg:gap-6 lg:flex-nowrap">
{cardDetails.map(({ image, title, description, button }) => (
<div
key={title}
className="flex flex-col min-w-[325px] max-w-[375px] lg:w-[496px] relative border border-neutral-200 rounded-3xl hover:shadow-xl active:shadow-none transition-shadow group"
>
<a
className="absolute inset-0 z-1 focus-visible:outline focus-visible:outline-offset focus-visible:rounded-3xl"
href="#"
aria-label={title}
/>
<img src={image} alt={title} className="object-cover h-auto rounded-t-3xl aspect-video" />
<div className="flex flex-col items-end p-4 grow">
<p className="font-medium typography-text-base">{title}</p>
<p className="mt-1 mb-4 font-normal typography-text-sm text-neutral-700">{description}</p>
<SfButton
size="sm"
variant="tertiary"
className="mt-auto group-has-[:focus-visible]:outline group-has-[:focus-visible]:outline-offset pointer-events-none"
tabIndex={-1}
>
{button}
</SfButton>
</div>
</div>
))}
</div>
);
}
Category Card
The category card view with a circle shaped image, and category title, clickable as the one element.
const categories = [
{
title: `Women`,
image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/women_category.png',
},
{
title: `Men`,
image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/men_category.png',
},
{
title: `Kids`,
image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/kid_category.png',
},
];
export default function CategoryCard() {
return (
<div className="flex flex-wrap gap-4 lg:gap-6 lg:flex-no-wrap">
{categories.map(({ title, image }) => (
<div className="relative min-w-[180px] flex-col max-w-[240px] group" key={title}>
<a
className="absolute w-full h-full z-[1] focus-visible:outline focus-visible:outline-offset focus-visible:rounded-3xl"
href="#"
aria-label={title}
/>
<img
className="transition-[filter] drop-shadow-none group-hover:drop-shadow-xl group-active:drop-shadow-none"
src={image}
alt={title}
width="240"
height="240"
/>
<div className="flex justify-center p-4 gap-4">
<a className="font-medium no-underline text-normal-900 typography-text-base group-hover:underline group-hover:text-primary-900 group-hover:font-normal group-active:underline group-active:text-primary-900 group-active:font-normal">
{title}
</a>
</div>
</div>
))}
</div>
);
}
Card Feature
This type of card has only button to interact.
import { SfButton, SfIconPackage, SfIconWarehouse, SfIconPublishedWithChanges } from '@storefront-ui/react';
import classNames from 'classnames';
const cardDetails = [
{
icon: <SfIconPackage size="2xl" />,
title: 'Free shipping',
description: 'Learn about our commitments to ethics, our team, our communities and more.',
buttonText: 'Read more',
isDisabled: false,
},
{
icon: <SfIconWarehouse size="2xl" />,
title: 'Click & Collect',
description: 'Learn about our commitments to ethics, our team, our communities and more.',
buttonText: 'Read more',
isDisabled: false,
},
{
icon: <SfIconPublishedWithChanges size="2xl" />,
title: 'Free 30-Day returns',
description: 'Learn about our commitments to ethics, our team, our communities and more.',
buttonText: 'Read more',
isDisabled: true,
},
];
export default function CardFeature() {
return (
<div className="flex flex-wrap gap-4 lg:gap-6 lg:flex-nowrap">
{cardDetails.map(({ icon, title, description, buttonText, isDisabled }) => (
<div key={title} className="flex flex-col w-full max-w-[325px] sm:w-[375px] lg:w-[496px] items-center">
<span className={classNames(isDisabled && 'text-disabled-900')}>{icon}</span>
<div className="p-4 flex flex-col items-center">
<p className={classNames('font-medium typography-text-base', { 'text-disabled-900': isDisabled })}>
{title}
</p>
<p
className={classNames('mt-1 mb-4 font-normal typography-text-sm text-neutral-700 text-center', {
'text-disabled-700': isDisabled,
})}
>
{description}
</p>
<SfButton size="sm" variant="secondary" disabled={isDisabled} className="mt-auto">
{buttonText}
</SfButton>
</div>
</div>
))}
</div>
);
}