Files
design-system/components/Card.astro

72 lines
1.6 KiB
Plaintext

---
/**
* Card — surface de contenu, cliquable ou non.
*
* Props :
* - href (string) : rend toute la carte cliquable (<a>) + hover élévation légère
* - class (string) : classes additionnelles
*
* Slots : media (image/visuel pleine largeur en tête), default (corps).
* Pas de border-left accent (loi Impeccable) : la hiérarchie passe par la
* surface, la bordure fine et l'élévation au hover.
*/
interface Props {
href?: string;
class?: string;
}
const { href, class: className } = Astro.props;
const Tag = href ? "a" : "article";
---
<Tag class:list={["tf-card", { "tf-card--link": href }, className]} href={href}>
{
Astro.slots.has("media") && (
<div class="tf-card__media">
<slot name="media" />
</div>
)
}
<div class="tf-card__body"><slot /></div>
</Tag>
<style>
.tf-card {
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-sm);
}
.tf-card--link {
color: inherit;
text-decoration: none;
transition: transform var(--dur-base) var(--ease-out),
box-shadow var(--dur-base) var(--ease-out),
border-color var(--dur-base) var(--ease-out);
}
.tf-card--link:hover {
color: inherit;
transform: translateY(-2px);
box-shadow: var(--shadow-md);
border-color: var(--border-strong);
}
.tf-card__media :global(img) {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.tf-card__body {
padding: var(--space-lg);
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
</style>