Files
design-system/components/Button.astro

143 lines
3.2 KiB
Plaintext

---
/**
* Button — bouton ou lien-bouton.
*
* Props :
* - variant ('primary' | 'secondary' | 'ghost' | 'link', 'primary')
* - size ('sm' | 'md' | 'lg', 'md') — sur écran tactile, sm remonte à 44px
* - href (string) : rend un <a> au lieu d'un <button>
* - type ('button' | 'submit' | 'reset', 'button') — ignoré si href
* - disabled (boolean, false) — sur <a> : aria-disabled + href retiré
* - class (string) : classes additionnelles
* + tout autre attribut est passé tel quel (aria-*, data-*, target…)
*/
interface Props {
variant?: "primary" | "secondary" | "ghost" | "link";
size?: "sm" | "md" | "lg";
href?: string;
type?: "button" | "submit" | "reset";
disabled?: boolean;
class?: string;
[key: string]: unknown;
}
const {
variant = "primary",
size = "md",
href,
type = "button",
disabled = false,
class: className,
...rest
} = Astro.props;
const Tag = href ? "a" : "button";
const attrs = href
? disabled
? { "aria-disabled": "true", tabindex: "-1" }
: { href }
: { type, disabled: disabled || undefined };
---
<Tag
class:list={["tf-btn", `tf-btn--${variant}`, `tf-btn--${size}`, className]}
{...attrs}
{...rest}
>
<slot />
</Tag>
<style>
.tf-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-xs);
min-height: 2.75rem; /* 44px — cible tactile */
padding-inline: var(--space-lg);
border: 1px solid transparent;
border-radius: var(--radius-md);
font-family: var(--font-body);
font-size: var(--fs-base);
font-weight: var(--fw-medium);
line-height: 1;
text-decoration: none;
cursor: pointer;
transition: background-color var(--dur-fast) var(--ease-out),
color var(--dur-fast) var(--ease-out),
border-color var(--dur-fast) var(--ease-out);
}
/* Tailles */
.tf-btn--sm {
min-height: 2.25rem;
padding-inline: var(--space-md);
font-size: var(--fs-sm);
}
.tf-btn--lg {
min-height: 3rem;
padding-inline: var(--space-xl);
font-size: var(--fs-lg);
}
/* Sur pointeur tactile, aucune taille ne descend sous 44px */
@media (pointer: coarse) {
.tf-btn--sm {
min-height: 2.75rem;
}
}
/* Variantes */
.tf-btn--primary {
background: var(--primary);
color: var(--on-primary);
}
.tf-btn--primary:hover {
background: var(--primary-hover);
color: var(--on-primary);
}
.tf-btn--secondary {
background: transparent;
color: var(--text);
border-color: var(--border-strong);
}
.tf-btn--secondary:hover {
background: var(--surface-2);
color: var(--text);
}
.tf-btn--ghost {
background: transparent;
color: var(--text);
}
.tf-btn--ghost:hover {
background: var(--surface-2);
color: var(--text);
}
.tf-btn--link {
background: transparent;
color: var(--link);
padding-inline: var(--space-2xs);
text-decoration: underline;
text-underline-offset: 0.2em;
}
.tf-btn--link:hover {
color: var(--link-hover);
}
/* États désactivés */
.tf-btn:disabled,
.tf-btn[aria-disabled="true"] {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
</style>