Card Button
The CardButton component is a flexible card-style link designed to display a title, icon, optional badge, subtitle, and feature list.
It supports active and inactive states, hover animations, custom styling, and configurable link targets.
import { CardButton } from "tawuniya/components";
👉 View in Storybook
Props​
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | - | The title displayed inside the card. |
icon | ReactNode | string | null | - | Icon displayed above the title. |
active | boolean | false | Indicates whether the card is active. Applies the primary border and active indicator. |
features | FeatureItem[] | [] | List of features displayed inside the card. |
subText | ReactNode | - | Optional secondary text displayed below the title when the card is inactive. |
href | string | null | - | Optional link URL. If omitted, the card behaves as a button and can be controlled via onClick. |
onClick | MouseEventHandler<HTMLAnchorElement | HTMLButtonElement> | - | Click handler for router-based navigation or custom logic. |
target | HTMLAttributeAnchorTarget | undefined | _blank | Specifies where to open the linked page. Only used when href is provided. |
badgeText | string | - | Optional text displayed inside a badge at the top of the card. |
type | "default" | "flashed" | "mobile" | "default" | Adjusts the visual behavior for the default, rotating-feature, or mobile card layouts. |
image | string | - | Optional illustrative image used for mobile layout cards. |
classNames | CardButtonClassNames | - | Allows custom classes to be applied to different parts of the component. |
FeatureItem​
type FeatureItem = {
key: string;
text: string;
};
| Property | Type | Description |
|---|---|---|
key | string | Unique identifier for the feature. |
text | string | Text displayed for the feature. |
classNames​
The classNames prop allows customization of individual sections of the component.
interface CardButtonClassNames {
rootWrapper?: string;
mainWrapper?: string;
contentWrapper?: string;
featuresWrapper?: string;
iconWrapper?: string;
title?: string;
badge?: {
wrapper?: string;
badgeItem?: string;
};
feature?: {
wrapper?: string;
icon?: string;
text?: string;
};
}
| Property | Type | Description |
|---|---|---|
rootWrapper | string | Custom classes for the root card wrapper. |
mainWrapper | string | Custom classes for the main card wrapper. |
contentWrapper | string | Custom classes for the content container. |
featuresWrapper | string | Custom classes for the features container. |
iconWrapper | string | Custom classes for the icon wrapper. |
title | string | Custom classes for the card title. |
badge.wrapper | string | Custom classes for the badge wrapper. |
badge.badgeItem | string | Custom classes for the badge itself. |
feature.wrapper | string | Custom classes for each feature wrapper. |
feature.icon | string | Custom classes for the feature icon. |
feature.text | string | Custom classes for the feature text. |
Usage​
Basic Example​
<CardButton
title="Comprehensive"
icon={<Icon name="car" />}
href="/comprehensive"
/>
Active Card​
Use the active prop to indicate the currently selected card.
<CardButton
title="Comprehensive"
icon={<Icon name="car" />}
href="/comprehensive"
active
/>
The active state:
- Applies the
border-primaryclass. - Changes the title and icon to the primary color.
- Displays an active indicator in the top-right corner.
- Expands the feature list if features are provided.
Flashed and Mobile Variants​
Use the type prop to switch to the alternating-feature or mobile presentation.
<CardButton
title="Motor"
icon={<Icon name="car" />}
href="/motor"
type="flashed"
features={[
{ key: "1", text: "Pay up-to-12 installments" },
{ key: "2", text: "Cash back with driver app" },
{ key: "3", text: "Insure online in seconds" },
]}
/>
<CardButton
title="Travel"
icon={<Icon name="shield" />}
href="/travel"
type="mobile"
image="/images/travel.png"
features={[
{ key: "1", text: "24/7 assistance" },
{ key: "2", text: "Global coverage" },
]}
/>
The mobile variant displays the provided image and keeps the headline compact while the flashed variant rotates through a single feature line with a subtle animation.
With Badge​
<CardButton
title="Premium"
icon={<Icon name="car" />}
href="/premium"
badgeText="Recommended"
/>
The badge is displayed above the card.
With Sub Text​
<CardButton
title="Basic"
icon={<Icon name="car" />}
href="/basic"
subText="Starting from SAR 500"
/>
The subText is displayed when the card is inactive.
When the user hovers over the card, the subtitle transitions out.
With Features​
<CardButton
title="Comprehensive"
icon={<Icon name="car" />}
href="/comprehensive"
features={[
{
key: "liability",
text: "Third Party Liability",
},
{
key: "roadside",
text: "Roadside Assistance",
},
{
key: "disaster",
text: "Natural Disaster Coverage",
},
]}
/>
Features are initially collapsed and become visible when the card is hovered.
Active Card With Features​
<CardButton
title="Comprehensive"
icon={<Icon name="car" />}
href="/comprehensive"
active
features={[
{
key: "liability",
text: "Third Party Liability",
},
{
key: "roadside",
text: "Roadside Assistance",
},
{
key: "disaster",
text: "Natural Disaster Coverage",
},
]}
/>
When the card is active, the features remain visible without requiring hover.
Click-only usage with router navigation​
When you already handle navigation with a router, href can be omitted and onClick can be used instead.
const navigate = useRouter();
<CardButton
title="Premium"
icon={<Icon name="car" />}
onClick={() => navigate("/premium")}
/>;
Custom Link Target​
The default target is _blank.
<CardButton
title="Premium"
icon={<Icon name="car" />}
href="/premium"
target="_self"
/>
Supported values are the standard HTML anchor targets:
"_blank";
"_self";
"_parent";
"_top";
Custom Styling​
<CardButton
title="Premium"
icon={<Icon name="car" />}
href="/premium"
classNames={{
mainWrapper: "max-w-64",
contentWrapper: "gap-y-4",
iconWrapper: "bg-muted p-2 rounded-full",
title: "text-lg",
featuresWrapper: "mt-2",
badge: {
wrapper: "top-0",
badgeItem: "bg-green-600",
},
feature: {
wrapper: "gap-x-2",
icon: "size-4",
text: "text-sm",
},
}}
/>
Security for external links​
When the card is used with target="_blank", the component adds rel="noopener noreferrer" automatically to reduce the risk of tabnabbing.
Complete Example​
<CardButton
title="Comprehensive"
icon={<Icon name="car" />}
href="/comprehensive"
target="_blank"
active
badgeText="Recommended"
subText="Starting from SAR 1,500"
features={[
{
key: "liability",
text: "Third Party Liability",
},
{
key: "roadside",
text: "Roadside Assistance",
},
{
key: "natural-disaster",
text: "Natural Disaster Coverage",
},
]}
/>
Variant notes​
default: standard card with hover-expanding feature listflashed: compact rotating feature highlight for landing pagesmobile: card configured for mobile-style promos with image support
Features​
- Active State: Supports active and inactive card states.
- Badge: Displays an optional badge above the card.
- Sub Text: Supports secondary content below the title.
- Feature List: Displays a list of included features.
- Hover Animation: Features and subtitle use smooth hover transitions.
- Custom Icons: Accepts any
ReactNodeas the card icon. - Custom Styling: Supports granular styling through the
classNamesprop. - Link Support: Works as a standard HTML anchor with configurable
hrefandtarget. - Responsive Width: Uses a maximum width while allowing custom width classes.
Notes​
hrefis optional. When omitted, the component renders as abuttonand can be used withonClickfor router-driven navigation.- The default
targetis_blankand is only applied whenhrefis present. featuresdefaults to an empty array.subTextis only rendered when the card is inactive.- Features are collapsed by default for inactive cards and expand on hover.
- Features remain expanded when the card is active.
- The
keyproperty in each feature should be unique. - Use the
classNamesprop when component-level customization is required without modifying the base component styles.