Skip to content
Craft & CSS

In defence of the button

Every framework ships a Button component, and every designer has opinions about it. States, semantics, and the disabled attribute everyone gets wrong — a field guide to the most important 24 pixels in your product.

Beginner2 min read (computed · recorded 8)updated 2026-09-10buttonsformsa11ycomponents

The button is a promise with states

A button has five states and each is a promise: rest (this will do something), hover (it knows you are here), active/pressed (it has heard you), focus (the keyboard can reach it), and disabled (it cannot — and here is the promise most products break). Users do not read documentation; they read states. A button whose states lie is a button that teaches users to distrust the whole product.

  • Rest: the label is a verb that names the outcome — 'Save changes', never 'Submit'. 'OK' is what a dialog says when it has given up explaining.
  • Hover: the change must be legible at a glance — background shift, slight lift — and must not be the only affordance on a touchscreen that has no hover.
  • Pressed: instant visual response (scale to 0.97, fill change) inside 60ms. The press state is the product saying 'I heard you' before the network says anything.
  • Focus: a visible ring that appears on keyboard focus and does not vanish on mouse click — and never, ever outline: none without a replacement.
  • Disabled: here is the rule — if the button is disabled because the form is incomplete, say what is missing instead of disabling. Disable only when the action is genuinely impossible right now (already saving, nothing selected), and always explain why, next to the button, in text.

Semantics are the API

button-semantics.html
<button type="button">            <!-- click, no form submit -->
<button type="submit">           <!-- the form's action -->
<button disabled>                <!-- genuinely unavailable -->
<a role="button" tabindex="0">   <!-- only if it navigates; else a <button> -->
<!-- A <div onClick> is a button that lost its keyboard,
     its focus ring, its screen-reader role, and its dignity. -->
The one you always forget

A button with no type attribute inside a form defaults to type='submit'. The 'settings' button that submits the search form and navigates away is a debugging classic — declare type='button' on every button that is not the submitter, and your users will stop losing their work.

The craft details

A button's padding is its hit area: the tap target needs 44×44px even when the visual is smaller, via padding or an invisible hit-slot. Its corners should match the product's radius scale, not the nearest trend. Its label should never wrap mid-phrase ('Save all' is fine; 'Save all changes to the current draft and' is a sentence wearing a button's clothes). And when the action is destructive, the button says what it will do — 'Delete comment', with the confirm as a second step, never a surprise.

Practise the lesson

Theory sticks when you ship it. These original Motif assets put this guide's lesson to work — open one and copy it into your own page.