143 lines
3.2 KiB
Plaintext
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>
|