Article Card
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.
npx manteen add @house/article-cardDependencies
@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.
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.
| Prop | Type | Default | Description |
|---|---|---|---|
image* | string | — | Cover image URL, rendered inside the linked card header. |
title* | string | — | Article title; also used as the image alt text and rendered as a link. |
description* | string | — | Summary text, clamped to four lines. |
authorName* | string | — | Byline shown in the card footer. |
authorAvatar | string | — | Avatar image URL for the byline; a placeholder avatar renders when omitted. |
rating | string | — | Text for the gradient badge over the image; no badge renders when omitted. |
href* | string | — | Destination for the image and title links. |
onLike | () => void | — | Renders the like action icon and handles its click. |
onBookmark | () => void | — | Renders the bookmark action icon and handles its click. |
onShare | () => void | — | Renders the share action icon and handles its click. |
...others | BoxProps & 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:
rootimageratingtitlefooteraction
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.