Skip to content

Article Card

ComponentMantine ≥ 9MantineProvider

Prop-driven article preview with author, rating and action affordances.

Install

One command copies the item's source into your project, where you own and edit it.

Terminal window
npx manteen add @house/article-card

Dependencies

  • @mantine/core@^9npm
  • @tabler/icons-react@^3npm

Installing this item also installs @house/mantine-ui-license from the registry.

Preview

Usage

A copy-ready example published by the registry author. Imports use the aliases Manteen configures at init.

article-card.usage.tsx
import { ArticleCard } from "@/components/ui/article-card";
export function FeaturedArticle() {
return (
<ArticleCard
image="https://images.unsplash.com/photo-1778084356053-40103587d24f?auto=format&fit=crop&w=1080&q=80"
title="How resilient teams design for change"
description="A field guide to building systems that remain clear, useful, and adaptable as the work evolves."
authorName="Avery Stone"
rating="4.9"
href="/articles/resilient-teams"
onLike={() => console.log("liked")}
onBookmark={() => console.log("bookmarked")}
onShare={() => console.log("shared")}
/>
);
}

Props

Author-documented props, carried verbatim from the registry item.

PropTypeDefaultDescription
image*stringCover image URL, rendered inside the linked card header.
title*stringArticle title; also used as the image alt text and rendered as a link.
description*stringSummary text, clamped to four lines.
authorName*stringByline shown in the card footer.
authorAvatarstringAvatar image URL for the byline; a placeholder avatar renders when omitted.
ratingstringText for the gradient badge over the image; no badge renders when omitted.
href*stringDestination for the image and title links.
onLike() => voidRenders the like action icon and handles its click.
onBookmark() => voidRenders the bookmark action icon and handles its click.
onShare() => voidRenders the share action icon and handles its click.
...othersBoxProps & ElementProps<"div", "title">All other props are forwarded to the root Card, after its own withBorder and radius="md", so both can be overridden. Mantine Styles API props (classNames, styles, unstyled) are also accepted.

* required

Styling

ArticleCard exposes these parts through the public classNames/styles interface:

  • root
  • image
  • rating
  • title
  • footer
  • action

Author-declared; Manteen reports the declaration but does not verify each selector is wired.

Source

2 installable files ship with this item.

registry/mantine-ui/article-card/article-card.tsxcomponent · 4.0 kB · tsx
/**
* Adapted from Mantine UI's ArticleCard at
* ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see
* LICENSES/MANTINE-UI.txt after installation.
*/
import {
ActionIcon,
Avatar,
Badge,
type BoxProps,
Card,
type ElementProps,
type Factory,
factory,
Group,
Image,
type StylesApiProps,
Text,
useProps,
useStyles,
} from "@mantine/core";
import { IconBookmarkFilled, IconHeartFilled, IconShare2 } from "@tabler/icons-react";
import classes from "./article-card.module.css";
export type ArticleCardStylesNames = "root" | "image" | "rating" | "title" | "footer" | "action";
export interface ArticleCardProps
extends BoxProps,
StylesApiProps<ArticleCardFactory>,
ElementProps<"div", "title"> {
image: string;
title: string;
description: string;
authorName: string;
authorAvatar?: string;
rating?: string;
href: string;
onLike?: () => void;
onBookmark?: () => void;
onShare?: () => void;
}
export type ArticleCardFactory = Factory<{
props: ArticleCardProps;
ref: HTMLDivElement;
stylesNames: ArticleCardStylesNames;
}>;
export const ArticleCard = factory<ArticleCardFactory>((_props) => {
const props = useProps("ArticleCard", null, _props);
const {
classNames,
className,
style,
styles,
unstyled,
vars,
attributes,
ref,
image,
title,
description,
authorName,
authorAvatar,
rating,
href,
onLike,
onBookmark,
onShare,
...others
} = props;
const getStyles = useStyles<ArticleCardFactory>({
name: "ArticleCard",
classes,
props,
className,
style,
classNames,
styles,
unstyled,
attributes,
vars,
});
const hasActions = Boolean(onLike || onBookmark || onShare);
return (
<Card ref={ref} withBorder radius="md" unstyled={unstyled} {...getStyles("root")} {...others}>
<Card.Section component="a" href={href}>
<Image src={image} height={180} alt={title} {...getStyles("image")} />
</Card.Section>
{rating && (
<Badge
{...getStyles("rating")}
variant="gradient"
gradient={{ from: "indigo.8", to: "violet.8", deg: 145 }}
>
{rating}
</Badge>
)}
<Text {...getStyles("title")} component="a" href={href}>
{title}
</Text>
<Text fz="sm" lineClamp={4} opacity={0.9}>
{description}
</Text>
<Group justify="space-between" {...getStyles("footer")}>
<Group gap="xs">
<Avatar src={authorAvatar} size={24} alt="" />
<Text fz="sm">{authorName}</Text>
</Group>
{hasActions && (
<Group gap={8}>
{onLike && (
<ActionIcon
variant="subtle"
{...getStyles("action")}
aria-label="Like"
onClick={onLike}
>
<IconHeartFilled size={16} color="var(--mantine-color-red-6)" />
</ActionIcon>
)}
{onBookmark && (
<ActionIcon
variant="subtle"
{...getStyles("action")}
aria-label="Bookmark"
onClick={onBookmark}
>
<IconBookmarkFilled size={16} color="var(--mantine-color-yellow-7)" />
</ActionIcon>
)}
{onShare && (
<ActionIcon
variant="subtle"
{...getStyles("action")}
aria-label="Share"
onClick={onShare}
>
<IconShare2 size={16} color="var(--mantine-color-cyan-6)" />
</ActionIcon>
)}
</Group>
)}
</Group>
</Card>
);
});
ArticleCard.classes = classes;
ArticleCard.displayName = "ArticleCard";
export namespace ArticleCard {
export type Props = ArticleCardProps;
export type StylesNames = ArticleCardStylesNames;
export type Factory = ArticleCardFactory;
}
registry/mantine-ui/article-card/article-card.module.cssstyle · 1.0 kB · css
/*
* Adapted from Mantine UI's ArticleCard at
* ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see
* LICENSES/MANTINE-UI.txt after installation.
*/
.root {
position: relative;
overflow: visible;
background-color: var(--mantine-color-body);
}
.rating {
position: absolute;
top: var(--mantine-spacing-xs);
right: 12px;
pointer-events: none;
}
.title {
display: block;
margin-top: var(--mantine-spacing-md);
margin-bottom: 7px;
color: var(--mantine-color-bright);
font-family: var(--mantine-font-family);
font-weight: 500;
}
.image {
width: calc(100% + 2px);
margin-top: -1px;
margin-right: -1px;
margin-left: -1px;
border-top-left-radius: var(--mantine-radius-md);
border-top-right-radius: var(--mantine-radius-md);
}
.action {
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5));
}
.action:hover {
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6));
}
.footer {
margin-top: var(--mantine-spacing-md);
}

Author notes

Adapted from Mantine UI ArticleCard at ffbf61c559…; demo data removed and replaced with typed props.