better cards
This commit is contained in:
63
site/src/components/StandardCard.astro
Normal file
63
site/src/components/StandardCard.astro
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
type Props = {
|
||||
href: string;
|
||||
title: string;
|
||||
summary?: string;
|
||||
imageUrl?: string;
|
||||
dateLabel?: string;
|
||||
viewsLabel?: string;
|
||||
sourceLabel: string;
|
||||
isExternal?: boolean;
|
||||
linkAttrs?: Record<string, any>;
|
||||
};
|
||||
|
||||
const {
|
||||
href,
|
||||
title,
|
||||
summary,
|
||||
imageUrl,
|
||||
dateLabel,
|
||||
viewsLabel,
|
||||
sourceLabel,
|
||||
isExternal,
|
||||
linkAttrs,
|
||||
} = Astro.props;
|
||||
|
||||
function truncate(s: string, n: number) {
|
||||
const t = (s || "").trim();
|
||||
if (!t) return "";
|
||||
if (t.length <= n) return t;
|
||||
// ASCII ellipsis to avoid encoding issues in generated HTML.
|
||||
return `${t.slice(0, Math.max(0, n - 3)).trimEnd()}...`;
|
||||
}
|
||||
|
||||
const summaryText = truncate(summary || "", 180);
|
||||
|
||||
---
|
||||
|
||||
<a
|
||||
class="card"
|
||||
href={href}
|
||||
target={isExternal ? "_blank" : undefined}
|
||||
rel={isExternal ? "noopener noreferrer" : undefined}
|
||||
{...(linkAttrs || {})}
|
||||
>
|
||||
<div class="card-media">
|
||||
{imageUrl ? <img src={imageUrl} alt="" loading="lazy" /> : <div class="card-placeholder" />}
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="card-content">
|
||||
<h3 class="card-title">{title}</h3>
|
||||
{summaryText ? <p class="card-summary">{summaryText}</p> : null}
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<span class="muted card-date">{dateLabel || ""}</span>
|
||||
<span class="muted card-views" aria-hidden={viewsLabel ? undefined : "true"}>
|
||||
{viewsLabel || ""}
|
||||
</span>
|
||||
<span class={`pill pill-${sourceLabel}`}>{sourceLabel}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
Reference in New Issue
Block a user