Button
Material Design 3 button component with multiple variants and sizes
Button
Buttons allow users to take actions with a single tap. @duskmoon-dev/core provides a complete set of Material Design 3 button variants.
Basic Usage
Basic Button
<button class="btn btn-primary">Primary Button</button>Variants
Filled Buttons
Filled buttons have the most visual impact and should be used for primary actions.
Filled Buttons
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-tertiary">Tertiary</button>Outlined Buttons
Outlined buttons are medium-emphasis buttons. They’re used for important but not primary actions.
Outlined Buttons
<button class="btn btn-outlined">Outlined</button>
<button class="btn btn-outlined btn-primary">Outlined Primary</button>
<button class="btn btn-outlined btn-secondary">Outlined Secondary</button>Text Buttons
Text buttons are used for low-priority actions, such as “Cancel” or “Learn More”.
Text Buttons
<button class="btn btn-text">Text Button</button>
<button class="btn btn-text btn-primary">Text Primary</button>Tonal Buttons
Tonal buttons use the theme’s container colors for a subtle but noticeable effect.
Tonal Buttons
<button class="btn btn-tonal">Tonal</button>
<button class="btn btn-tonal btn-primary">Tonal Primary</button>
<button class="btn btn-tonal btn-secondary">Tonal Secondary</button>Semantic Colors
Use semantic colors for user feedback:
Semantic Color Buttons
<button class="btn btn-success">Success</button>
<button class="btn btn-error">Error</button>
<button class="btn btn-warning">Warning</button>
<button class="btn btn-info">Info</button>Semantic colors work with all variants:
Semantic Colors with Variants
<button class="btn btn-outlined btn-success">Success Outlined</button>
<button class="btn btn-text btn-error">Error Text</button>
<button class="btn btn-tonal btn-warning">Warning Tonal</button>Sizes
Three sizes are available:
Button Sizes
<button class="btn btn-primary btn-sm">Small</button>
<button class="btn btn-primary btn-md">Medium (default)</button>
<button class="btn btn-primary btn-lg">Large</button>Icon Buttons
Buttons with Icons
Add icons alongside text:
Buttons with Icons
<button class="btn btn-primary">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Add Item
</button>
<button class="btn btn-outlined">
Download
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</button>Icon-Only Buttons
For compact interfaces, use icon-only buttons:
Icon-Only Buttons
<button class="btn btn-icon btn-primary">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<button class="btn btn-icon btn-icon-sm btn-outlined">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
</button>
<button class="btn btn-icon btn-icon-lg btn-tonal">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
</button>States
Disabled State
Disabled buttons are non-interactive:
Disabled Buttons
<button class="btn btn-primary" disabled>Disabled</button>
<button class="btn btn-outlined" disabled>Disabled Outlined</button>
<button class="btn btn-text" disabled>Disabled Text</button>Loading State
Use the btn-loading class to show a loading spinner. The text is hidden and a spinner appears in its place:
Loading Buttons
<button class="btn btn-primary btn-loading">Loading</button>
<button class="btn btn-secondary btn-loading">Loading</button>
<button class="btn btn-outlined btn-loading">Loading</button>
<button class="btn btn-tonal btn-loading">Loading</button>The loading state can be combined with any button variant and size:
Loading Button Sizes
<button class="btn btn-primary btn-sm btn-loading">Small</button>
<button class="btn btn-primary btn-md btn-loading">Medium</button>
<button class="btn btn-primary btn-lg btn-loading">Large</button>Block Buttons
Full-width buttons:
Block Button
<button class="btn btn-primary btn-block">Block Button</button>Button Groups
Group related buttons together:
Button Group
<div class="flex gap-2">
<button class="btn btn-text">Cancel</button>
<button class="btn btn-outlined">Save Draft</button>
<button class="btn btn-primary">Publish</button>
</div>Best Practices
Visual Hierarchy
Use button variants to create clear visual hierarchy:
- Primary action:
btn-primary(filled) - Secondary action:
btn-outlinedorbtn-tonal - Tertiary action:
btn-text
Visual Hierarchy Example
<div class="flex gap-2">
<button class="btn btn-text">Cancel</button>
<button class="btn btn-outlined">Save Draft</button>
<button class="btn btn-primary">Publish</button>
</div>Accessibility
- Always provide meaningful button text or
aria-labelfor icon buttons - Use semantic HTML
<button>elements - Ensure sufficient color contrast
- Support keyboard navigation (built-in)
Accessible Icon Button
<button class="btn btn-icon btn-primary" aria-label="Close dialog">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>Touch Targets
Ensure buttons are large enough for touch interfaces (minimum 44×44px):
Responsive Button for Touch
<button class="btn btn-primary btn-md md:btn-sm">
Responsive Button
</button>Framework Examples
React
interface ButtonProps {
variant?: 'primary' | 'outlined' | 'text' | 'tonal';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: () => void;
}
export function Button({ variant = 'primary', size = 'md', children, ...props }: ButtonProps) {
return (
<button className={`btn btn-${variant} btn-${size}`} {...props}>
{children}
</button>
);
}
// Usage
<Button variant="primary" onClick={() => console.log('clicked')}>
Click Me
</Button>
Vue
<template>
<button :class="`btn btn-${variant} btn-${size}`" v-bind="$attrs">
<slot />
</button>
</template>
<script setup>
defineProps({
variant: {
type: String,
default: 'primary'
},
size: {
type: String,
default: 'md'
}
})
</script>
<!-- Usage -->
<Button variant="primary" @click="handleClick">
Click Me
</Button>
API Reference
Class Names
| Class | Description |
|---|---|
.btn | Base button styles (required) |
.btn-primary | Primary filled button (default) |
.btn-secondary | Secondary filled button |
.btn-tertiary | Tertiary filled button |
.btn-outlined | Outlined button variant |
.btn-text | Text button variant |
.btn-tonal | Tonal button variant |
.btn-success | Success semantic color |
.btn-error | Error semantic color |
.btn-warning | Warning semantic color |
.btn-info | Info semantic color |
.btn-sm | Small size |
.btn-md | Medium size (default) |
.btn-lg | Large size |
.btn-icon | Icon-only button |
.btn-icon-sm | Small icon button |
.btn-icon-lg | Large icon button |
.btn-block | Full-width button |
.btn-loading | Loading state with spinner |
.btn-circle | Circular button (1:1 aspect ratio) |
.btn-square | Square button (1:1 aspect ratio) |
Combinations
You can combine classes for different effects:
Class Combinations
<!-- Outlined secondary button, large size -->
<button class="btn btn-outlined btn-secondary btn-lg">
Large Outlined Secondary
</button>
<!-- Tonal error button, small size -->
<button class="btn btn-tonal btn-error btn-sm">
Small Tonal Error
</button>