Button Component
A ready-to-use button component for Astro projects with built-in support for multiple variants, sizes, and automatic link rendering.
---
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<'button'> {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
href?: string;
target?: string;
rel?: string;
isExternal?: boolean;
}
const {
variant = 'primary',
size = 'md',
href,
target,
rel,
isExternal,
class: className,
...rest
} = Astro.props;
const Tag = href ? 'a' : 'button';
const finalRel = isExternal ? (rel || 'noopener noreferrer') : rel;
const finalTarget = isExternal ? (target || '_blank') : target;
const elementProps = href
? { href, target: finalTarget, rel: finalRel, ...rest }
: { type: (rest.type || 'button'), ...rest };
---
<Tag
class:list={['btn', `btn-${variant}`, `btn-${size}`, className]}
{...elementProps}
>
<slot />
</Tag>/* button.scss */
$variants: (
"primary": #007bff,
"secondary": #6c757d,
"outline": transparent,
"ghost": transparent
);
.btn {
display: inline-flex;
border-radius: 4px;
font-weight: 500;
// Size Mapping
&-sm { padding: 0.25rem 0.5rem; }
&-md { padding: 0.5rem 1rem; }
&-lg { padding: 0.75rem 1.5rem; }
// Dynamic Variant Generation
@each $name, $color in $variants {
&-#{$name} {
background-color: $color;
@if $name == "outline" {
border: 1px solid #007bff;
color: #007bff;
} @else if $name == "ghost" {
color: inherit;
} @else {
color: #fff;
}
}
}
}