Buttons

Button

Starts an action, from saving a form to deleting a site.

Refined portVue source: WildButton.vueStorybook story: Buttons/ButtonReact component: WBtn

Try it

Hover it, press it and tab to it. Turn on Loading to see the busy state.

Variant
Size
Shape
Disabled
Loading
<WildButton
  variant="primary"
  size="md"
  shape="rounded"
>
  Save changes
</WildButton>

When to use

  • Use a button to start an action: save, send, export, delete.
  • Use one primary button per view, for the action that moves the work forward.
  • Use destructive for actions that delete or can’t be undone, and confirm them with a confirm dialog.
Don’t use it
  • To go to another page. Use a text link.
  • For an action shown as an icon alone. Use Icon button.

Variants

Primary, secondary and borders set emphasis. Destructive, success and warning carry meaning, so use them only when the action does.

primary
secondary
borders
destructive
success
warning
<WildButton variant="primary">Save changes</WildButton>
<WildButton variant="secondary">Cancel</WildButton>
<WildButton variant="borders">Export</WildButton>

Sizes

Five heights, from 24 to 44px. The default is md, at 40px.

2xs · 24
xs · 32
sm · 36
md · 40
lg · 44
<WildButton size="2xs">Upload</WildButton>
<WildButton size="sm">Upload</WildButton>
<WildButton size="lg">Upload</WildButton>
Use 2xs only for actions inside dense table rows, where a taller button would break the row height.

States

Rest, hover, pressed, keyboard focus and disabled, for every variant. Disabled is one flat grey on all six, so it never reads as a choice of colour.

rest
hover
active
focus
disabled
primary
secondary
borders
destructive
success
warning
Keyboard focus is a ring outside the button, so it shows on every fill. Borders keeps its edge when disabled.
loading
processing
success
error
selected
<WildButton loading>Saving</WildButton>
<WildButton processing>Working</WildButton>
<WildButton success>Saved</WildButton>
<WildButton error>Failed</WildButton>
Use loading while the click is being handled. Use processing when the work carries on and its progress shows elsewhere.

Content

  • Start with a verb: Save, Export, Invite.
  • Name the object when the verb alone is ambiguous: Delete site, Send order.
  • Use sentence case, and keep it to one to three words.

Accessibility

A button that shows only an icon needs ariaLabel. A disabled button leaves the tab order; a loading one keeps focus and announces that it’s busy.

KeyWhat it does
TabMoves focus to the button.
Enter / SpaceRuns the action.

Props

PropTypeDefaultDescription
variant'primary' | 'secondary' | 'borders' | 'destructive' | 'success' | 'warning''primary'Emphasis or meaning. See Variants.
size'2xs' | 'xs' | 'sm' | 'md' | 'lg''md'Height: 24, 32, 36, 40 or 44px.
shape'rounded' | 'pill''rounded'6px corners, or fully round.
iconstring—Icon name, shown beside the label.
iconPosition'left' | 'right''right'Which side of the label the icon sits.
iconOnlybooleanfalseHides the label. Prefer Icon button.
disabledbooleanfalseBlocks the action and leaves the tab order.
loadingbooleanfalseSpinner over the label; blocks repeat clicks.
processingbooleanfalseDots after the label while work continues.
successbooleanfalseA tick once the action succeeds.
errorbooleanfalseA cross once the action fails.
selectedbooleanfalsePressed look, for a button that toggles.
type'button' | 'submit' | 'reset''button'Native button type.
ariaLabelstring—Accessible name. Required with iconOnly.

Tokens

TokenValueWhere it lands
--radius-button6pxCorners in the rounded shape.
--color-primary#00AEEFPrimary fill.
--color-primary-hover#0ABEFFPrimary fill on hover.
--color-primary-active#1877F2Primary fill when pressed, and its focus ring.
--color-error#E12121Destructive fill and the error state.
--global-neutral-grey-200#F5F5F5Disabled fill, every variant.
--global-neutral-grey-800#ADACB0Disabled label and icon.
--global-neutral-grey-500#E6E6E6Disabled border, borders variant only.
--font-weight-medium475Label weight.
(no token)2px gap, 2px ringKeyboard focus, outside the button.

Icon button

A button with an icon and no label, for compact actions in toolbars and table rows.

Refined portVue source: WildIconButton.vueStorybook story: Buttons/Icon ButtonReact component: WIconBtn

Try it

Hover it, press it and tab to it. Each press turns it on or off.

Variant
Size
Disabled
<WildIconButton
  icon="plus"
  variant="ghost"
  size="md"
  ariaLabel="Add"
/>

When to use

  • Use an icon button for a familiar action that repeats, such as an action on every table row.
  • Use active for a button that stays switched on until it’s pressed again.
Don’t use it
  • For an action people need to read to understand. Use a Button with a label.

Variants

Use ghost inside a row, subtle in a toolbar, and solid for the one action that must be found without hunting.

ghost
subtle
solid
<WildIconButton variant="ghost" icon="plus" ariaLabel="Add" />
<WildIconButton variant="subtle" icon="plus" ariaLabel="Add" />
<WildIconButton variant="solid" icon="plus" ariaLabel="Add" />
Ghost has no resting fill, so a column of row actions doesn’t turn into a column of boxes.

Sizes

Four square sizes, from 24 to 40px.

xs · 24
sm · 28
md · 32
lg · 40
Icon and text buttons share only the 24, 32 and 40px heights. Use one of those when the two sit side by side.

States

Rest, hover, pressed and keyboard focus, for every variant.

rest
hover
active
focus
ghost
subtle
solid
A press steps one shade past hover. Keyboard focus is a ring outside the button, so it shows on every fill.
ghost · on
subtle · on
solid · on
disabled
<WildIconButton icon="plus" active ariaLabel="Add" />
<WildIconButton icon="plus" disabled ariaLabel="Add" />
On turns the icon global-blue-1000 on ghost and subtle. Solid takes the cyan fill instead, with a near-black icon.

Accessibility

Give every icon button an ariaLabel, because the icon is all it shows. With active set, screen readers hear it as a toggle, pressed or not.

KeyWhat it does
TabMoves focus to the button. A disabled one is skipped.
Enter / SpaceRuns the action, or turns it on or off.

Props

PropTypeDefaultDescription
iconstring—Icon name.
variant'ghost' | 'subtle' | 'solid''ghost'Emphasis. See Variants.
size'xs' | 'sm' | 'md' | 'lg''sm'Square size: 24, 28, 32 or 40px.
activeboolean—Switched on. Announced as a pressed toggle.
disabledbooleanfalseBlocks the action and leaves the tab order.
ariaLabelstring—Accessible name. Always required.

Tokens

TokenValueWhere it lands
--radius-button6pxCorners at every size.
--color-grey-800#7F7D83Ghost icon at rest.
--color-grey-200#F1F1F1Ghost fill on hover.
--color-grey-300#ECECEDGhost fill when pressed, and when on.
--color-grey-1000#2D2B32Solid fill.
--global-blue-1000#1877F2The ghost and subtle icon when on: 3.59:1 on the ghost’s grey fill, 3.96:1 on the subtle’s blue-100. Cyan was 2.14:1 and 2.37:1.
--global-blue-100#EBFAFFSubtle fill when on.
--color-primary#00AEEFSolid fill when on. Its icon is grey-1300 #0A090B, 7.85:1, because white is 2.53:1.
--color-primary-active#1877F2Focus ring.
(no token)2px gap, 2px ringKeyboard focus, outside the button.

Segmented controller

Switches between a few views, ranges or sites, as tabs or pills, with every option in view.

Refined portVue source: WildSegmentedController.vueStorybook story: Buttons/Segmented ControllerReact component: WSeg

Try it

Pick a segment, or tab in and use the arrow keys. Turn off Interactive to see the display-only row.

Variant
Size
Orientation
Full width
Interactive
<WildSegmentedController
  v-model="value"
  :tabs="ranges"
  variant="underline"
  size="md"
/>

When to use

  • Use underline to switch between views of one subject, such as Readings and Alarms.
  • Use pills to re-cut the same view, such as a chart’s window: 24 h, 7 days, 30 days.
  • Use tabs to switch between things of one kind, such as four sites.
Don’t use it

Variants

Underline is the default. Pills sit in a recessed track, and tabs mark the chosen one with a bold label over a sliding bar.

underline
pills
tabs
<WildSegmentedController v-model="view"
  :tabs="views" variant="underline" />
<WildSegmentedController v-model="range"
  :tabs="ranges" variant="pills" />
<WildSegmentedController v-model="site"
  :tabs="sites" variant="tabs" />

Entity tabs

Give each tab a status dot, so a site that stopped reporting shows before anyone opens it.

<WildSegmentedController v-model="site" variant="tabs" full-width
  :tabs="[{ id: 'b12', label: 'Bore 12', dot: 'error' }, …]" />
Entity tabs list things; view tabs list ways of looking at one. Keep them in separate strips, so people know what they’re changing.

Sizes

Three, with 12, 13 and 14px labels. Tabs are 32, 40 and 44px tall. The default is md.

sm
md
lg
tabs · sm
tabs · md
tabs · lg

Orientation and width

Stack the options in a column for a settings panel. Use full width to run the rule edge to edge, or to share the width between pills.

vertical tabs
vertical pills
full width, underline
full width, pills

Badges and disabled segments

Add a count when there’s something to look at before switching. Keep an unavailable segment in place and disabled, so the others don’t move.

Accessibility

With a change handler it’s a tab list, and each segment is announced as a tab, selected or not. Without one it’s a row of labels that takes no focus.

KeyWhat it does
TabMoves focus to the chosen segment.
← / →Chooses the previous or next segment, skipping disabled ones.
↑ / ↓The same, in a vertical strip.
Home / EndChooses the first or last segment.

Props

PropTypeDefaultDescription
v-modelstring—The chosen segment’s id.
tabs{ id, label, badge?, dot?, icon?, disabled?, title? }[]—The segments, in order.
variant'underline' | 'pills' | 'tabs''underline'See Variants.
size'sm' | 'md' | 'lg''md'Label size: 12, 13 or 14px.
orientation'horizontal' | 'vertical''horizontal'A row, or a column.
full-widthbooleanfalseSpans the container.
labelstring—Accessible name for the tab list.

Tokens

TokenValueWhere it lands
--global-blue-1000#1877F2Underline and tabs bar.
--global-blue-300#BDEDFFUnderline hover hint.
--global-blue-1100#0C296AChosen label in underline.
--global-neutral-grey-1300#0A090BChosen label in pills and tabs.
--global-neutral-grey-1000#4F4D55Resting label, every variant.
--global-neutral-grey-500#E6E6E6Underline rule.
--global-neutral-grey-400#ECECEDPills track and tabs rule.
--color-white#FFFFFFChosen pill.
--shadow-xs0 1px 2px 0 rgba(10,9,11,.05)Chosen pill’s lift.
--radius-full9999pxPills track and segments.
(no token)9px / 6pxVertical pills track and segments.
--font-weight-medium475Resting label.
(no token)550 / 600Chosen label; 600 in tabs.
(no token)2.5pxTabs bar.
(no token)7pxStatus dot.