Button
A Button triggers an action when the user clicks or taps on it.
Guidelines
Using buttons
Buttons must contain a label and can also include an icon. For icon-only buttons, the label is visually hidden but available to assistive technology users.
Use the Button component when you want to trigger an action or toggle something in the user's current context.
Avoid using buttons when:
- The action is to navigate the user to a new resource, which would take them away from the current context. In such cases, consider Link instead.[1]
- You need to represent state-persistent user actions. In this case, use ToggleButton instead.
Specifications
The button styles distinguish types of buttons and each button’s state (e.g. hover, active, focussed) in accessible color variations.
- Icon (optional)
Icons simplify user recognition and provide the ability to shorten button labels to a minimum. - Label
Button labels should be as short as possible, with text that clearly states what action follows clicking the button (e.g. download, submit form, search).
When using buttons, consider the size of their target area. Make sure that the associated active touch area is at least 44 x 44 dp. Otherwise users may fail to hit the active area, for example due to motor disabilities.
Refer to the Button component in Codex Figma.
Types
Button content
Depending on the content of the button, there are the following types:
- Text with icon
It combines descriptive text with a reinforcing icon. It's suitable when you need to convey meaning through both textual and visual elements. - Text-only
It contains only text for simplicity. It is suitable for actions that do not require or benefit from an accompanying icon, or when the icon would not effectively reinforce the meaning of the text. - Icon-only
It uses an icon alone to create visual impact. It is suitable for actions that can be universally recognized through the icon alone, such as the “edit” action.
Button action or variant (“flavor”)
There three type of buttons based on the type of action they convey:
- Neutral
Use neutral buttons for actions that are neutral or secondary in importance. - Progressive
Use progressive buttons for actions that lead to the next step in the process. - Destructive
Reserve destructive buttons for actions that involve removal or limitation, such as deleting a page or blocking a user. Avoid using them for actions like cancel buttons.
Button weight
Buttons can be categorized into three types based on their weight and their significance in their respective contexts:
Primary buttons
Primary buttons signal the main action in a given view – a page or a dialog. As they should guide the user to the most important action (“call to action”), there should only be one primary button per view.
They come in two variants (“flavors”):
- Progressive: Use progressive buttons for actions that lead to the next step in the process.
- Destructive: Use destructive buttons for actions that remove or limit, such as deleting a page or blocking a user. Don't use it for actions such as cancel buttons.
Normal buttons
When designing a project, normal framed buttons are the default choice. Quiet, frameless buttons (read next section) should only be used in views, where normal buttons need to be further visually de-emphasized, see below.
Normal buttons have three variants (“flavors”):
- Neutral
- Progressive
- Destructive
Quiet buttons (“frameless”)
Use quiet buttons for an easily recognizable action that does not detract focus from the content. For example, the icon-only edit buttons alongside sections in article view on mobile Wikipedia.[2]They still feature minimal target sizes and states to provide the user clear interaction feedback. Normal (framed) buttons are the default choice for simplified recognition.
Quiet buttons have three variants (“flavors”):
- Neutral
- Progressive
- Destructive
Interaction states
Buttons have the following visually separate states:
Neutral buttons
Progressive buttons
Destructive buttons
- Default
- Hover
- Active
- Focus
- Disabled
Accessibility note: The disabled state does not meet our minimum color contrast rules. WCAG 2.1 states that “…part[s] of an inactive user interface component […] have no contrast requirement”.[3]
Provide sufficient information in a disabled element’s context, so the user can understand what is disabled and how to enable it (if applicable).
References
- “Links vs. Buttons in Modern Web Applications” by Marcy Sutton
- Mobile English Wikipedia: Button (computing) article with exemplified quiet edit buttons
- Web Content Accessibility Guidelines (WCAG) 2.1 – Success Criterion 1.4.3 Contrast (Minimum)
Demos
Configurable
<cdx-button>Click me</cdx-button>
Name | Value |
---|---|
Props | |
action | |
weight | |
size | |
disabled | |
Slots | |
default | |
View | |
Reading direction |
Default
<cdx-button>Click me</cdx-button>
Button actions
There are three button actions: default, progressive, and destructive.
<div>
<cdx-button>Default button</cdx-button>
</div>
<div>
<cdx-button action="progressive">Progressive button</cdx-button>
</div>
<div>
<cdx-button action="destructive">Destructive button</cdx-button>
</div>
Button weights
There are three button weights: normal, primary, and quiet.
<div>
<cdx-button action="progressive">
Normal progressive button
</cdx-button>
</div>
<div>
<cdx-button action="progressive" weight="primary">
Primary progressive button
</cdx-button>
</div>
<div>
<cdx-button action="progressive" weight="quiet">
Quiet progressive button
</cdx-button>
</div>
Disabled
Add the disabled
attribute for a disabled button.
<cdx-button disabled>Disabled button</cdx-button>
With icon
<template>
<cdx-button @click="onClick">
<cdx-icon :icon="cdxIconArrowPrevious" /> Go back
</cdx-button>
</template>
<script>
import { defineComponent } from 'vue';
import { CdxButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconArrowPrevious } from '@wikimedia/codex-icons';
export default defineComponent( {
name: 'ButtonWithIcon',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconArrowPrevious,
onClick
};
}
} );
</script>
<template>
<cdx-button @click="onClick">
<cdx-icon :icon="cdxIconArrowPrevious"></cdx-icon> Go back
</cdx-button>
</template>
<script>
const { defineComponent } = require( 'vue' );
const { CdxButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconArrowPrevious } = require( './icons.json' );
// @vue/component
module.exports = defineComponent( {
name: 'ButtonWithIcon',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconArrowPrevious,
onClick
};
}
} );
</script>
<template>
<cdx-button weight="quiet" @click="onClick">
<cdx-icon :icon="cdxIconDownload" /> Download
</cdx-button>
</template>
<script>
import { defineComponent } from 'vue';
import { CdxButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconDownload } from '@wikimedia/codex-icons';
export default defineComponent( {
name: 'QuietButtonWithIcon',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconDownload,
onClick
};
}
} );
</script>
<template>
<cdx-button weight="quiet" @click="onClick">
<cdx-icon :icon="cdxIconDownload"></cdx-icon> Download
</cdx-button>
</template>
<script>
const { defineComponent } = require( 'vue' );
const { CdxButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconDownload } = require( './icons.json' );
// @vue/component
module.exports = defineComponent( {
name: 'QuietButtonWithIcon',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconDownload,
onClick
};
}
} );
</script>
Icon-only button
WARNING
Due to the lack of descriptive text, icon-only buttons require one of the following attributes: aria-label
or aria-hidden
.
The attribute aria-label
has to be used on icon-only buttons to be understandable by assistive technology users. Exceptions are buttons in component combinations, e.g. the button in the Combobox component, that are skipped by adding aria-hidden
without negatively impacting the component's functionality.
<template>
<cdx-button aria-label="Back" @click="onClick">
<cdx-icon :icon="cdxIconPrevious" />
</cdx-button>
</template>
<script>
import { defineComponent } from 'vue';
import { CdxButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconPrevious } from '@wikimedia/codex-icons';
export default defineComponent( {
name: 'IconOnlyButton',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconPrevious,
onClick
};
}
} );
</script>
<template>
<cdx-button aria-label="Back" @click="onClick">
<cdx-icon :icon="cdxIconPrevious"></cdx-icon>
</cdx-button>
</template>
<script>
const { defineComponent } = require( 'vue' );
const { CdxButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconPrevious } = require( './icons.json' );
// @vue/component
module.exports = defineComponent( {
name: 'IconOnlyButton',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconPrevious,
onClick
};
}
} );
</script>
<template>
<cdx-button
weight="quiet"
aria-label="Help"
@click="onClick"
>
<cdx-icon :icon="cdxIconHelp" />
</cdx-button>
</template>
<script>
import { defineComponent } from 'vue';
import { CdxButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconHelp } from '@wikimedia/codex-icons';
export default defineComponent( {
name: 'QuietIconOnlyButton',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconHelp,
onClick
};
}
} );
</script>
<template>
<cdx-button
weight="quiet"
aria-label="Help"
@click="onClick"
>
<cdx-icon :icon="cdxIconHelp"></cdx-icon>
</cdx-button>
</template>
<script>
const { defineComponent } = require( 'vue' );
const { CdxButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconHelp } = require( './icons.json' );
// @vue/component
module.exports = defineComponent( {
name: 'QuietIconOnlyButton',
components: {
CdxButton,
CdxIcon
},
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconHelp,
onClick
};
}
} );
</script>
Button sizes
There are two button sizes: medium and large.
Most buttons should use the medium size. The large size is intended only for accessibility purposes, such as making icon-only buttons larger on touchscreens to increase the touch area.
Size | Min-size | Text button | Icon-only button |
---|---|---|---|
Medium | 32px | ||
Large | 44px |
<template>
<table class="cdx-demo-button-sizes">
<caption>
Button size examples for all supported <code>size</code> values.
</caption>
<thead>
<th>Size</th>
<th>Min-size</th>
<th>Text button</th>
<th>Icon-only button</th>
</thead>
<tr>
<td>
<span>Medium</span>
</td>
<td>
<span>32px</span>
</td>
<td>
<cdx-button @click="onClick">
Medium button
</cdx-button>
</td>
<td>
<cdx-button aria-label="Medium button example" @click="onClick">
<cdx-icon :icon="cdxIconBell" />
</cdx-button>
</td>
</tr>
<tr>
<td>
<span>Large</span>
</td>
<td>
<span>44px</span>
</td>
<td>
<cdx-button size="large" @click="onClick">
Large button
</cdx-button>
</td>
<td>
<cdx-button
aria-label="Large button example"
size="large"
@click="onClick"
>
<cdx-icon :icon="cdxIconBell" />
</cdx-button>
</td>
</tr>
</table>
</template>
<script>
import { defineComponent } from 'vue';
import { CdxButton, CdxIcon } from '@wikimedia/codex';
import { cdxIconBell } from '@wikimedia/codex-icons';
export default defineComponent( {
name: 'ButtonSizes',
components: { CdxButton, CdxIcon },
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconBell,
onClick
};
}
} );
</script>
<style lang="less">
@import ( reference ) '@wikimedia/codex-design-tokens/theme-wikimedia-ui.less';
.cdx-demo-button-sizes {
table {
width: @size-full;
}
caption {
margin-top: @spacing-25;
text-align: left;
caption-side: bottom;
}
}
</style>
<template>
<table class="cdx-demo-button-sizes">
<caption>
Button size examples for all supported <code>size</code> values.
</caption>
<thead>
<th>Size</th>
<th>Min-size</th>
<th>Text button</th>
<th>Icon-only button</th>
</thead>
<tr>
<td>
<span>Medium</span>
</td>
<td>
<span>32px</span>
</td>
<td>
<cdx-button @click="onClick">
Medium button
</cdx-button>
</td>
<td>
<cdx-button aria-label="Medium button example" @click="onClick">
<cdx-icon :icon="cdxIconBell"></cdx-icon>
</cdx-button>
</td>
</tr>
<tr>
<td>
<span>Large</span>
</td>
<td>
<span>44px</span>
</td>
<td>
<cdx-button size="large" @click="onClick">
Large button
</cdx-button>
</td>
<td>
<cdx-button
aria-label="Large button example"
size="large"
@click="onClick"
>
<cdx-icon :icon="cdxIconBell"></cdx-icon>
</cdx-button>
</td>
</tr>
</table>
</template>
<script>
const { defineComponent } = require( 'vue' );
const { CdxButton, CdxIcon } = require( '@wikimedia/codex' );
const { cdxIconBell } = require( './icons.json' );
// @vue/component
module.exports = defineComponent( {
name: 'ButtonSizes',
components: { CdxButton, CdxIcon },
setup() {
function onClick() {
// eslint-disable-next-line no-console
console.log( 'click event emitted' );
}
return {
cdxIconBell,
onClick
};
}
} );
</script>
<style lang="less">
@import 'mediawiki.skin.variables.less';
.cdx-demo-button-sizes {
table {
width: @size-full;
}
caption {
margin-top: @spacing-25;
text-align: left;
caption-side: bottom;
}
}
</style>
Vue usage
Props
Prop name | Description | Type | Values | Default |
---|---|---|---|---|
action | The kind of action that will be taken on click. | ButtonAction | 'default' , 'progressive' , 'destructive' | 'default' |
weight | Visual prominence of the button. | ButtonWeight | 'normal' , 'primary' , 'quiet' | 'normal' |
size | Button size. Most buttons should use the default medium size. In rare cases the large size should be used, for example to make icon-only buttons larger on touchscreens. | ButtonSize | 'medium' , 'large' | 'medium' |
Events
Event name | Properties | Description |
---|---|---|
click |
Slots
Name | Description | Bindings |
---|---|---|
default | Button content |
CSS-only version
Markup structure
The CSS Button component uses the <button>
element and the .cdx-button
class.
<!-- Button element with CSS class(es). -->
<button class="cdx-button">Click me</button>
Button actions
There are three button actions: default, progressive, and destructive. Use the following classes to apply these actions:
- Default:
cdx-button--action-default
(class can be omitted since this is the default) - Progressive:
cdx-button--action-progressive
- Destructive:
cdx-button--action-destructive
<div>
<button class="cdx-button cdx-button--action-default">Default button</button>
</div>
<div>
<button class="cdx-button cdx-button--action-progressive">
Progressive button
</button>
</div>
<div>
<button class="cdx-button cdx-button--action-destructive">
Destructive button
</button>
</div>
Button weights
There are three button weights: normal, primary, and quiet. Use the following classes to apply these actions:
- Normal:
cdx-button--weight-normal
(class can be omitted since this is the default) - Primary:
cdx-button--weight-primary
- Quiet:
cdx-button--weight-quiet
<div>
<button class="cdx-button cdx-button--action-progressive">
Progressive normal button
</button>
</div>
<div>
<button
class="cdx-button cdx-button--action-progressive cdx-button--weight-primary"
>
Progressive primary button
</button>
</div>
<div>
<button
class="cdx-button cdx-button--action-progressive cdx-button--weight-quiet"
>
Progressive quiet button
</button>
</div>
Disabled
Add the disabled
attribute to the <button>
element to get a disabled element with appropriate styles.
<button class="cdx-button" disabled>Disabled button</button>
With CSS icon
You can use CSS icons within button content. To set up an icon within a CSS-only button, do the following:
- Add an empty span inside the button before the label with the class
cdx-button__icon
. - Use the
.cdx-mixin-css-icon()
mixin with the@param-is-button-icon
parameter set totrue
.
Note that in Firefox version 52 and below, full color support for icons inside CSS-only buttons is not available, and the icon will fall back to a single color.
WARNING
Be sure to add aria-hidden="true"
to the icon element to hide it from assistive technology.
<button class="cdx-button">
<span
class="cdx-button__icon cdx-demo-css-icon--arrow-previous"
aria-hidden="true"
></span>
Go back
</button>
// Note: you must import the design tokens before importing the css-icon mixin
@import (reference) "@wikimedia/codex-design-tokens/theme-wikimedia-ui.less";
@import (reference) "@wikimedia/codex/mixins/css-icon.less";
.cdx-demo-css-icon--arrow-previous {
.cdx-mixin-css-icon(@cdx-icon-arrow-previous, @param-is-button-icon: true);
}
@import "mediawiki.skin.variables.less";
.cdx-demo-css-icon--arrow-previous {
.cdx-mixin-css-icon(@cdx-icon-arrow-previous, @param-is-button-icon: true);
}
Icon-only button
Add the cdx-button--icon-only
class for an icon-only button.
WARNING
Be sure to add an aria-label
attribute to the button element so it can be understandable to screen reader users.
<button class="cdx-button cdx-button--icon-only" aria-label="Back">
<span class="cdx-button__icon cdx-demo-css-icon--arrow-previous"></span>
</button>
// Note: you must import the design tokens before importing the css-icon mixin
@import (reference) "@wikimedia/codex-design-tokens/theme-wikimedia-ui.less";
@import (reference) "@wikimedia/codex/mixins/css-icon.less";
.cdx-demo-css-icon--arrow-previous {
.cdx-mixin-css-icon(@cdx-icon-arrow-previous, @param-is-button-icon: true);
}
@import "mediawiki.skin.variables.less";
.cdx-demo-css-icon--arrow-previous {
.cdx-mixin-css-icon(@cdx-icon-arrow-previous, @param-is-button-icon: true);
}
Button sizes
There are two button sizes: medium and large. Most buttons should use the medium size. The large size is intended only for accessibility purposes, such as making icon-only buttons larger on small screens to increase the touch area.
Use the following classes to apply these actions:
- Medium:
cdx-button--size-medium
(class can be omitted since this is the default) - Large:
cdx-button--size-large
<div>
<button class="cdx-button cdx-button--icon-only" aria-label="Back">
<span class="cdx-button__icon cdx-demo-css-icon--bell"></span>
</button>
</div>
<div>
<button
class="cdx-button cdx-button--icon-only cdx-button--size-large"
aria-label="Back"
>
<span class="cdx-button__icon cdx-demo-css-icon--bell"></span>
</button>
</div>
// Note: you must import the design tokens before importing the css-icon mixin
@import (reference) "@wikimedia/codex-design-tokens/theme-wikimedia-ui.less";
@import (reference) "@wikimedia/codex/mixins/css-icon.less";
.cdx-demo-css-icon--bell {
.cdx-mixin-css-icon(@cdx-icon-bell, @param-is-button-icon: true);
}
@import "mediawiki.skin.variables.less";
.cdx-demo-css-icon--bell {
.cdx-mixin-css-icon(@cdx-icon-bell, @param-is-button-icon: true);
}
Link buttons and other elements
DANGER
Do not do this unless you absolutely have to. Use a <button>
element styled like a button for an action, and use an <a>
element styled like a link for navigation.
There are rare occasions where an inline element other than <button>
needs to be styled to look like a button. To achieve this, add the following classes to your inline element:
- The classes detailed above:
cdx-button
, plus any other classes needed for action, weight, or size cdx-button--fake-button
- Either
cdx-button--fake-button--enabled
for an enabled button orcdx-button--fake-button--disabled
for a disabled button. You must include one of these classes to get the proper button styles.
If your element behaves like a button (triggering an action or event), you should also add role="button"
to the element if that role is allowed. See the ARIA Authoring Practices Guide on buttons for more information.
<a
class="cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--action-progressive"
href="#"
>
Progressive link button
</a>