A square button carrying an icon and no label. It is VButton underneath, with two defaults of its own and a required name, since the picture is all a screen reader would otherwise get.
size sets the box, square at each of the five steps from 24 to 56 pixels. compact takes 4px off both sides, so the box stays square.
vue
<script setup lang="ts">
import { VIconButton } from 'vectis-ui'
import { search } from 'vectis-ui/icons'
const sizes = ['xs', 'sm', 'md', 'lg', 'xl'] as const
</script>
<template>
<div class="row">
<VIconButton
v-for="size in sizes"
:key="size"
:icon="search"
:size="size"
variant="outline"
:label="`Search, ${size}`"
/>
</div>
<!-- Compact takes 4px off both sides, so the box stays square and the icon inside it
keeps the size its step gives it. -->
<div class="row">
<VIconButton
v-for="size in sizes"
:key="size"
:icon="search"
:size="size"
compact
variant="outline"
:label="`Search, ${size} compact`"
/>
</div>
</template>
<style scoped>
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--vectis-space-3);
}
</style>
Shapes
shape chooses the silhouette, square or circular. Inside a VButtonGroup the row's own corner rules win and a circular segment keeps square seams.
vue
<script setup lang="ts">
import { VButtonGroup, VIconButton } from 'vectis-ui'
import {
arrow_downward as arrowDownward,
arrow_upward as arrowUpward,
close,
swap_vert as swapVert,
} from 'vectis-ui/icons'
const sizes = ['sm', 'md', 'lg'] as const
</script>
<template>
<div class="stack">
<div class="row">
<VIconButton
v-for="size in sizes"
:key="size"
:icon="close"
:size="size"
variant="outline"
:label="`Close, ${size}`"
/>
<VIconButton
v-for="size in sizes"
:key="`${size}-circular`"
:icon="close"
:size="size"
shape="circular"
variant="outline"
:label="`Close, circular ${size}`"
/>
</div>
<!-- Inside a group the corner rules are the row's, and they win: a circular segment
keeps square seams so the row still reads as one object. Intended, and the
reason a shape is worth choosing before grouping rather than after. -->
<VButtonGroup variant="outline">
<VIconButton :icon="arrowUpward" shape="circular" label="Sort ascending" />
<VIconButton :icon="swapVert" shape="circular" label="Unsorted" />
<VIconButton :icon="arrowDownward" shape="circular" label="Sort descending" />
</VButtonGroup>
</div>
</template>
<style scoped>
.stack {
display: flex;
flex-direction: column;
gap: var(--vectis-space-5);
align-items: start;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--vectis-space-3);
}
</style>
Icons
icon takes any icon value and iconFilled asks for its filled form. The default slot is the way to an icon the prop cannot express, and stays decorative: the button is named by its label.
vue
<script setup lang="ts">
import { VIcon, VIconButton } from 'vectis-ui'
import { ICON_VIEW_BOX, code, notifications, search } from 'vectis-ui/icons'
import firefoxLogo from '~/assets/img/firefox-browser-svg.svg'
</script>
<template>
<!-- `icon` takes everything an icon prop in the library takes: one of its own icons,
a bare name for your resolver, or an explicit render. -->
<VIconButton :icon="search" variant="outline" label="Search" />
<VIconButton icon="translate" variant="outline" label="Translate" />
<VIconButton :icon="{ path: code.paths[0] }" variant="outline" label="View the source" />
<VIconButton :icon="{ src: firefoxLogo }" variant="outline" label="Open in Firefox" />
<!-- Filled marks a state, here a notification that has arrived. -->
<VIconButton :icon="notifications" icon-filled variant="outline" label="3 new notifications" />
<!-- The slot is the way to an icon the prop cannot express: a VIcon you configure
yourself, or an inline SVG. The button is already named by its label, so whatever
goes in here stays decorative. -->
<VIconButton variant="outline" label="Notifications, larger glyph">
<VIcon :name="notifications" :size="28" />
</VIconButton>
<VIconButton variant="outline" label="Add">
<svg :viewBox="ICON_VIEW_BOX" fill="none" stroke="currentColor" stroke-width="80">
<path d="M480-800v640M160-480h640" stroke-linecap="round" />
</svg>
</VIconButton>
</template>
As a link
href renders the button as an anchor, on the terms of VButton. A disabled link keeps its place and loses its destination.
vue
<script setup lang="ts">
import { VIconButton } from 'vectis-ui'
import { arrow_right_alt as arrowRightAlt, code } from 'vectis-ui/icons'
</script>
<template>
<!-- `href` renders an <a> instead of a <button>. -->
<VIconButton href="#usage" :icon="arrowRightAlt" variant="outline" label="Back to usage" />
<VIconButton
href="https://github.com"
target="_blank"
rel="noreferrer"
:icon="code"
variant="outline"
label="View the source, opens in a new tab"
/>
<!-- A disabled link keeps its place and loses its destination: the href is removed
and the clicks are dropped, since nothing in HTML disables an anchor. -->
<VIconButton href="#usage" :icon="arrowRightAlt" variant="outline" disabled label="Unavailable" />
</template>
States
disabled greys the button out through the colour tokens. loading puts a spinner in the icon's own box and disables the button while it turns.
vue
<script setup lang="ts">
import { VIconButton } from 'vectis-ui'
import { notifications } from 'vectis-ui/icons'
const variants = ['solid', 'soft', 'outline', 'ghost'] as const
</script>
<template>
<div class="row">
<VIconButton
v-for="variant in variants"
:key="variant"
:icon="notifications"
:variant="variant"
disabled
:label="`Notifications, ${variant} disabled`"
/>
</div>
<!-- Loading puts a spinner where the icon was and disables the button while it turns,
so the same action cannot be asked for twice. The spinner takes the icon's box,
which is what keeps the square from changing size. -->
<div class="row">
<VIconButton :icon="notifications" loading label="Refreshing" />
<VIconButton :icon="notifications" variant="outline" loading label="Checking" />
<VIconButton :icon="notifications" variant="ghost" loading label="Loading" />
</div>
</template>
<style scoped>
.row {
display: flex;
flex-wrap: wrap;
gap: var(--vectis-space-3);
}
</style>
API
Props
Prop
Type
Default
label
string
none
What the button does, in words. It becomes the aria-label and is the only thing a screen reader has to go on, so it names the action, "Close", "Next month", rather than the picture.
How much visual weight the button carries, on the VButton scale. Inside a VButtonGroup the group decides it.
tone
ButtonTone'accent' | 'neutral' | 'danger'
'neutral'
What the action means, in colour. An icon-only button is usually chrome, which is why it starts neutral where VButton starts accent. Left out inside a VButtonGroup it takes the group's tone.
elevated
boolean
false
Raises the button with a shadow, and a raised surface on ghost and outline.
size
ButtonSize'xs' | 'sm' | 'md' | 'lg' | 'xl'
'md'
The size of the square, taken from the scale shared by every control.
compact
boolean
false
Takes 4px off both sides of the square, which stays square.
shape
IconButtonShape'square' | 'circular'
'square'
The silhouette: a square carrying the corner radius every control shares, or a circle. The box itself is square either way, only the corners change.
href
string
none
Turns the button into an <a> pointing at this address. A disabled or loading link becomes inert: the address is dropped, so it can be neither focused nor followed.
type
ButtonHTMLAttributes['type']
'button'
The native type of the button. It is ignored as soon as href makes it a link.
disabled
boolean
false
Makes the button unusable, greyed out through the colour tokens.
loading
boolean
false
Replaces the icon with a spinner and disables the button while it turns.
The icon to show. The default slot is the way to supply one this prop cannot express.
iconFilled
boolean
false
Renders the icon in its filled form, the font's FILL axis.
Slots
Slot
Type
default
{}
The icon, when the icon prop cannot express it: a VIcon, or an inline SVG marked aria-hidden, the button being already named by its label.
Types
The types the tables above name, written as the library declares them. The ones carrying export can be imported from vectis-ui to type your own code; the others are the shape of what a slot hands out.