72 lines
1.6 KiB
Plaintext
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>
|