AdsButton
Three weights. Accent for the one action the screen exists to produce, default for everything else, quiet for actions that should not compete.
<AdsButton variant="accent" icon="plus">New crawl</AdsButton>
<AdsButton>Export CSV</AdsButton>
<AdsButton variant="quiet">Cancel</AdsButton>
<AdsButton destructive>Delete project</AdsButton>| Prop | Type | Default | Description |
|---|---|---|---|
variant | default | accent | quiet | default | |
size | sm | md | md | |
icon | string | — | Glyph name from utils/glyphs. |
loading | boolean | false | Spinner in place of the icon; sets aria-busy. |
destructive | boolean | false | Never accent: forces the default weight. Label it with the verb (Delete project). |
disabled | boolean | false |
Source · ads/app/components/AdsButton.vue
<script setup lang="ts">
// The ADS button. `.btn` is the raised soft surface; the accent variant is the
// one action per screen, so spend it once. A destructive action never gets the
// accent — the accent means "the thing to do", and deleting is not it — so
// `destructive` pins the button to the default weight whatever variant is
// asked for. The confirm that follows it says the verb, not "OK".
const props = withDefaults(
defineProps<{
variant?: 'default' | 'accent' | 'quiet'
size?: 'sm' | 'md'
icon?: string
iconSize?: number
loading?: boolean
destructive?: boolean
type?: 'button' | 'submit' | 'reset'
disabled?: boolean
}>(),
{ variant: 'default', size: 'md', iconSize: 16, loading: false, destructive: false, type: 'button', disabled: false },
)
const weight = computed(() => (props.destructive && props.variant === 'accent' ? 'default' : props.variant))
if (false && props.destructive && props.variant === 'accent') {
console.warn('[AdsButton] A destructive action cannot take the accent; rendering the default weight.')
}
</script>
<template>
<button
:type="type"
class="btn"
:class="{
'btn-accent': weight === 'accent',
'btn-quiet': weight === 'quiet',
'btn-sm': size === 'sm',
}"
:disabled="disabled || loading"
:aria-busy="loading || undefined"
>
<span v-if="loading" class="ads-spinner ads-button__spinner" aria-hidden="true" />
<Icon v-else-if="icon" :name="icon" :size="iconSize" />
<slot />
</button>
</template>
<style scoped>
.btn .ads-button__spinner {
width: 14px;
height: 14px;
border-width: 1.5px;
}
</style>