react-inclusive-card
Version:
A simple, accessible, and flexible card component for React, inspired by the excellent book ["Inclusive Components"](https://inclusive-components.design/cards) by Heydon Pickering.
147 lines (146 loc) • 5.63 kB
TypeScript
import "./InclusiveCard.css";
import * as React from "react";
type TRequiredChildren = Required<React.PropsWithChildren<object>>;
type TImageProps = {
/**
* The source URL of the image
*/
src: string;
/**
* If your image is decorative, pass and empty string
*/
alt: string;
customImageComponent?: never;
} | {
src?: never;
alt?: never;
/**
* A custom image component to render instead of a regular image
*/
customImageComponent: React.ReactElement;
};
type TOtherProps<TElement extends React.ElementType> = Omit<React.ComponentPropsWithoutRef<TElement>, "as" | "children" | "className">;
type TCardProps<TElement extends React.ElementType = "article"> = TRequiredChildren & TOtherProps<TElement> & {
/**
* The element type of the card,
* @default "article"
*/
as?: TElement;
className?: string;
/**
* extend the `className` of the card content area
* @default "inclusive-card__content"
*/
contentClassName?: string;
/**
* extend the `className` of the card image area
* @default "inclusive-card__image"
*/
imageClassName?: string;
/**
* Pass either `src` and `alt` or a custom image component.
* - If you want to use a custom image component, pass it as `customImageComponent` prop.
* - If you want to use a regular image, pass `src` and `alt` props.
*/
imageProps: TImageProps;
};
/**
* The Card component is a flexible and accessible card layout component
*
* It consists of an image and content area.
* you can specify the element type for the card using the `as` prop.
*/
declare const Card: {
<TElement extends React.ElementType>({ as, className, imageClassName, contentClassName, children, imageProps, ...rest }: TCardProps<TElement>): import("react/jsx-runtime").JSX.Element;
/**
* The `<Card.Title>` component is used to render the title of the card,
* it contains a link and it makes the whole card clickable.
*
* You can pass a custom link component using the `customLinkComponent` prop,
* or just pass a `href` prop to use the default `<a>` element.
*/
Title: <TElement extends Extract<React.ElementType, "h1" | "h2" | "h3" | "h4" | "h5" | "h6">>({ as, href, children, className, customLinkComponent, "aria-describedby": ariaDescribedBy, linkProps, ...rest }: TCardTitleProps<TElement>) => import("react/jsx-runtime").JSX.Element;
/**
* Wrap your interactive elements (like buttons and links) with `<Card.Controls>`
* to raise them above the card clickable area.
*
* It renders a `<div>` by default,
* - you can either change it to any other element using the `as` prop
* - or you can use the `asChild` prop to render the child element instead of the `<div>`
*
* ! Only one child element is allowed for the `<Card.Controls>` component when `asChild` is set to `true`
*/
Controls: React.FC<TCardControlsProps>;
/**
* Wrap your "call to action" element with `<Card.CallToAction>` to hide it from screen readers.
* With clickable card, the call-to-action element should be rather decorative, providing additional context to the card link.
*
* The Card.CallToAction component gets focus styles when the `<Card.Title>` link is focused.
* You can customize a `box-shadow` & `text-decoration` using CSS custom properties:
* - `--inclusive-card-cta-focus-decoration`
* - `--inclusive-card-cta-focus-shadow`
*
* Or you can override default styles in your CSS file using selector:
* ```css
* .inclusive-card__title:focus-within ~ .inclusive-card__cta {
* Your custom styles here
* }
* ```
*
* You can add an `aria-describedby` attribute to the Card.Title to attach a call-to-action text to the card link.
*
* @example with default `<Card.Title>` link:
* ```jsx
* <Card as="article">
* <Card.Title href="/some-link" aria-describedby="desc-example-title">Example Title</Card.Title>
* <Card.CallToAction id="desc-example-title">Click here to learn more</Card.CallToAction>
* </Card>
* ```
*
* @example with custom `<Card.Title>` link:
* ```jsx
* const MyCustomLink = ({ children, ...props }) => (
* <Link aria-describedby="desc-example-title" {...props}>
* {children}
* </Link>
* );
*
* <Card as="article">
* <Card.Title customLinkComponent={MyCustomLink}>
* Example Title
* </Card.Title>
* <Card.CallToAction id="desc-example-title">Click here to learn more</Card.CallToAction>
* </Card>
*/
CallToAction: React.FC<TCallToActionProps>;
};
type TLinkProps = {
customLinkComponent: React.ElementType;
href?: never;
linkProps?: never;
} | {
customLinkComponent?: never;
href: string;
linkProps?: Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href">;
};
type TCardTitleProps<TElement extends Extract<React.ElementType, "h1" | "h2" | "h3" | "h4" | "h5" | "h6">> = TRequiredChildren & TOtherProps<TElement> & TLinkProps & {
as?: TElement;
className?: string;
};
type TChildrenWithProps = React.ReactNode & {
props?: {
className?: string;
};
};
type TCardControlsProps = TOtherProps<"div"> & {
children: TChildrenWithProps;
as?: React.ElementType;
className?: string;
asChild?: boolean;
};
type TSpanProps = React.HTMLAttributes<HTMLSpanElement>;
type TCallToActionProps = TSpanProps & {
children: React.ReactNode;
className?: string;
};
export { Card };