68 lines
2.2 KiB
Svelte
68 lines
2.2 KiB
Svelte
<!-- [DEF:Button:Component] -->
|
|
<!--
|
|
@TIER: TRIVIAL
|
|
@SEMANTICS: button, ui-atom, interactive
|
|
@PURPOSE: Standardized button component with variants and loading states.
|
|
@LAYER: Atom
|
|
|
|
@INVARIANT: Always uses Tailwind for styling.
|
|
@INVARIANT: Supports accessible labels and keyboard navigation.
|
|
-->
|
|
|
|
<script>
|
|
// [SECTION: IMPORTS]
|
|
import { cn } from '$lib/utils.js';
|
|
// [/SECTION: IMPORTS]
|
|
|
|
// [SECTION: PROPS]
|
|
/**
|
|
* @purpose Define component interface and default values (Svelte 5 Runes).
|
|
*/
|
|
let {
|
|
variant = 'primary',
|
|
size = 'md',
|
|
isLoading = false,
|
|
disabled = false,
|
|
type = 'button',
|
|
class: className = '',
|
|
children,
|
|
onclick,
|
|
...rest
|
|
} = $props();
|
|
// [/SECTION: PROPS]
|
|
|
|
const baseStyles = 'inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 rounded-md';
|
|
|
|
const variants = {
|
|
primary: 'bg-primary text-white hover:bg-primary-hover focus-visible:ring-primary-ring',
|
|
secondary: 'bg-secondary text-secondary-text hover:bg-secondary-hover focus-visible:ring-secondary-ring',
|
|
danger: 'bg-destructive text-white hover:bg-destructive-hover focus-visible:ring-destructive-ring',
|
|
ghost: 'bg-transparent hover:bg-ghost-hover text-ghost-text focus-visible:ring-ghost-ring',
|
|
};
|
|
|
|
const sizes = {
|
|
sm: 'h-8 px-3 text-xs',
|
|
md: 'h-10 px-4 py-2 text-sm',
|
|
lg: 'h-12 px-6 text-base',
|
|
};
|
|
</script>
|
|
|
|
<!-- [SECTION: TEMPLATE] -->
|
|
<button
|
|
{type}
|
|
class={cn(baseStyles, variants[variant], sizes[size], className)}
|
|
disabled={disabled || isLoading}
|
|
{onclick}
|
|
{...rest}
|
|
>
|
|
{#if isLoading}
|
|
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-current" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
{/if}
|
|
{@render children?.()}
|
|
</button>
|
|
<!-- [/SECTION: TEMPLATE] -->
|
|
|
|
<!-- [/DEF:Button:Component] --> |