UNPKG

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
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 };