Skip to content
Bugwise
Menu
UI Components

Button

The most-tapped component in any app. Learn the emphasis levels, the six states every button has, and the bugs that create double orders and endless spinners.

30 min Inputs & selection iOS · Android · Web Also called: CTA, Action button, Call to action

You tap Place order in a food delivery app. Nothing seems to happen, so you tap again. Ten minutes later two pizzas arrive and your card was charged twice. That’s a buttonButtonA control the user taps or clicks to perform an action, like Save, Send or Buy.Open in glossary → bug, and it’s one of the most expensive bugs a team can ship.

Buttons look like the simplest thing on the screen. But every button has several states, talks to a server, and has to work with touch, mouse, keyboard and screen readers. That’s a lot of places for bugs to hide.

What is a button?

A button is a control that starts an action when you tap or click it: pay, send, save, delete, add to basket. The label tells you what will happen, and the look tells you how important the action is.

You’ll see buttons used to:

  • Commit an action: Place order, Send money, Book flight.
  • Move forward in a flow: Continue, Next, Sign up.
  • Do something small in place: Like, Share, Add another item.
  • Destroy something: Delete account, Remove card, Cancel subscription.

Anatomy

Add to basket1234
  1. 1

    Container

    The shape and fill. Its color and style signal how important the action is.

  2. 2

    Label

    A short verb that says exactly what happens: "Add to basket", not "OK".

  3. 3

    Icon (optional)

    Helps scanning. Never the only cue unless the icon is universally understood.

  4. 4

    Touch target

    The tappable area. It can be larger than the visible shape: at least 44×44 pt (iOS) or 48×48 dp (Android).

How it differs from similar components

A common finding is a button that should have been something else, or something else pretending to be a button.

ComponentWhat happens on tapKeeps a state?Typical use
ButtonRuns an actionNo (it doesn’t stay “on”)Pay, Save, Send, Delete
LinkNavigates to another page or screenNo“View order details”, “Terms of service”
Toggle switchTurns a setting on or off immediatelyYes, on/offNotifications, Wi-Fi
Segmented controlPicks one of a few viewsYes, one selectedList / Map
ChipFilters or adds a tagOften“Vegan”, “Under €20”
Menu itemRuns an action from a listNoOverflow menu: Rename, Duplicate

Designs & variants

Buttons come in emphasis levels. The more important the action, the louder the button. Each platform has its own names for them.

Pick a platform, then hover, press and Tab through the buttons. They all respond.

Primary · Filled

High emphasis

The single most important action on the screen: Pay, Place order, Continue. Usually one per screen.

Secondary · Tinted

Medium emphasis

Important, but not the main path: Save for later, Add another item. Softer fill, same shape.

Secondary · Outline (custom)

Medium emphasis

Outline only. Common on the web for Cancel or Back next to a primary button.

Tertiary · Plain

Low emphasis

Text only: Skip, Learn more, Forgot password? It still needs a full-size touch target, even without a visible shape.

Destructive

All

Red for actions that delete or can't be undone. It should almost always lead to a confirmation or an Undo.

Icon buttons

All

Save space in toolbars. Each one needs an accessible name, or a screen reader just says 'button'.

Sizes

iOS

Small, medium and large. Small buttons still need a 44 pt / 48 dp touch target around them.

Floating action button (FAB)

Android · Material 3

Floats above content for the screen's main action, like New order or Compose. iOS has no FAB; it uses a toolbar button instead.

Trail running shoes

Size 42 · Ocean blue

Total

€89.90

Sticky / floating CTA

All

A call-to-action pinned to the bottom of the screen. Check it never hides content, the keyboard, or the home indicator.

Button pair

iOS

Primary + secondary side by side. Check the order follows the platform (web dialogs often put the primary on the right).

EmphasisiOSAndroid (Material 3)Web (typical)Use it for
HighFilled (.borderedProminent)FilledPrimaryThe main action. One per screen.
MediumTinted / Gray (.bordered)Filled tonal, OutlinedSecondaryAlternatives: Save for later, Cancel
LowPlain (.borderless)TextTertiary, link-styleSkip, Learn more, Forgot password?
Destructiverole: .destructive (red)Error color (custom)DangerDelete, Remove, Cancel subscription
FloatingNot usedFAB, extended FABRareThe screen’s main creation action

Six states, every time

A button is not one picture. It’s six. Designers often deliver only the default look, so developers invent the rest, and that’s where bugs appear. Explore them here:

🎛️ Button states explorer

Every button has six states. A tester checks all of them on every platform and in both themes.

Platform

Emphasis

Default

Readable label, enough contrast (4.5:1 text, 3:1 shape against the background).

Hover

Web and iPad/desktop pointers only. A subtle change; never the only hint that something is clickable.

Pressed

Instant feedback on touch-down, so the user knows the tap registered.

Focused

Keyboard users must see a clear focus ring. Press Tab on the web and look for it.

Disabled

Looks inactive and really is: no tap, no click, no Enter key, no request.

Loading

Shows progress, blocks repeat taps, keeps its size, and always ends (success or error).

Now try a real one

Server response

Tap the button several times quickly while it loads. A good button counts your taps but sends only one request.

Ready

taps: 0requests sent: 0

When to use it

A button is the right choice when:

  • Tapping it performs an action (saves, sends, pays, deletes) rather than just showing another page.
  • The action has a clear verb you can put on the label.
  • There is a clear hierarchy: one primary action, maybe a secondary one, and the rest as low-emphasis buttons.
  • The action needs feedback: a loading state, a success message or an error.

Good and bad uses

Button for an action, link for navigation

View delivery terms

Placing an order does something. Reading the terms goes somewhere.

Buttons that only navigate

“Click here” says nothing, and navigation dressed up as a button confuses screen reader users and breaks Ctrl/Cmd-click on the web.

One clear primary action

The eye goes straight to the main action.

Everything shouts

Three primary buttons compete. Users hesitate or tap the wrong one.

Specific verb labels

You know exactly what each button does, even without reading the dialog text.

Vague labels

“Remove this card? Cancel / OK”. Does Cancel cancel the removal or remove the card? Users have to guess.

Big enough to hit

Tap area: 44×44 pt. The icon is small, the target is not.

The visible icon can be small, but the touch area must be at least 44×44 pt (iOS) or 48×48 dp (Android).

Tiny tap area

Tap area: 18×18 px. Only the icon itself reacts.

Users tap next to it, nothing happens, and they think the app is frozen.

Common bugs

These are the button bugs testers report again and again. Many of them cost real money.

#BugWhat you seeTypical severity
1Double submitTwo quick taps create two orders, two payments or two messagesCritical
2Endless spinnerAfter a network error the button keeps loading forever, with no message and no retryHigh
3Disabled button still worksA greyed-out button can still be tapped, clicked or triggered with EnterHigh
4Never enabledThe button stays disabled even after the form is valid, often after autofill or pasteHigh
5Label truncated“Place ord…” or the price cut off in other languages or with large textMedium
6Hidden CTAThe sticky button sits under the keyboard, the home indicator or a cookie bannerHigh
7No confirmation for destructive actions“Delete account” or “Clear basket” happens instantly with no confirm or undoHigh
8Tap area too smallOnly the icon reacts, not the whole visible button. Users miss itMedium
9No pressed or focus feedbackNothing changes on tap, or keyboard users can’t see which button is focusedMedium
10Layout jump on loadingThe button shrinks to the spinner’s size, so the layout jumps and the next tap lands on something elseLow–Medium

Find the bug

Here’s a food delivery checkout with six bugs. Test it like a real user on a bad day:

  • Tap buttons once, then tap them twice quickly.
  • Turn on Server errors and try to order.
  • Try buttons that look disabled.
  • Change the device settings, including the developer option.
  • Watch the network log and the order counter.

🐞 Bug hunt: Checkout screen

This demo has 6 hidden bugs. Explore it like a tester, then check your findings.

Found0/6
9:41
MenuCheckout

Your basket

  • 🍕Margherita pizza€11.501
  • 🥖Garlic bread€4.901
  • 🍋Lemonade€7.002

Promo code

Payment

Visa •••• 4242

Subtotal€23.40

Delivery€1.50

Device settings

Network logOrders in database: 0
  • Requests will appear here…
  1. 1

    Hint: Tap Place order twice quickly and watch the order counter.

  2. 2

    Hint: Turn on Server errors, then place an order.

  3. 3

    Hint: Leave the promo code empty and tap Apply.

  4. 4

    Hint: Turn on Large text and read the main button.

  5. 5

    Hint: Imagine your thumb slips onto Clear.

  6. 6

    Hint: Tap the edge of a + circle, then turn on Show layout bounds.

How to test it

Checklist

Button test checklist

0 of 14 checked

Example test cases

TC-BTN-001Placing an order creates exactly one order

Preconditions

  • User is logged in
  • Basket contains at least one item
  • Valid payment method is saved

Steps

  1. Open Checkout
  2. Tap "Place order"

Expected result

The button shows a loading state, then the "Order placed" screen appears. Exactly one order is visible in Order history.

TC-BTN-002Rapid double tap does not create a duplicate order

Preconditions

  • Network throttled to a slow 3G profile
  • Proxy tool (e.g. Charles) is recording traffic

Steps

  1. Open Checkout
  2. Tap "Place order" twice within 1 second

Expected result

Only one POST /api/orders request is sent. One order is created and the card is charged once.

TC-BTN-003Server error ends the loading state with a message

Preconditions

  • Backend returns 500 for POST /api/orders (mock or test environment)

Steps

  1. Open Checkout
  2. Tap "Place order"
  3. Wait for the response

Expected result

The spinner stops, an error message explains the problem, and "Place order" can be tapped again. No order is created.

TC-BTN-004Disabled button cannot be triggered

Preconditions

  • Promo code field is empty

Steps

  1. Tap "Apply"
  2. Web: Tab to "Apply" and press Enter and Space

Expected result

Nothing happens: no request is sent and no message appears. Screen reader announces "Apply, dimmed" (iOS) or "disabled".

TC-BTN-005Destructive action asks for confirmation

Steps

  1. Add 3 items to the basket
  2. Tap "Clear"

Expected result

A confirmation ("Remove all 3 items?") with a clearly labeled destructive option appears, or an Undo snackbar is shown. Items are not lost by one accidental tap.

TC-BTN-006Label is readable at the largest text size

Preconditions

  • iOS: Settings › Accessibility › Larger Text at maximum / Android: Font size at maximum

Steps

  1. Open Checkout
  2. Read the "Place order" button

Expected result

The full label, including the price, is readable. The button may grow taller or wrap the text.

TC-BTN-007Sticky CTA stays reachable with the keyboard open

Steps

  1. Open Checkout
  2. Tap the promo code field so the keyboard appears

Expected result

"Place order" moves above the keyboard or the screen can be scrolled to reach it. It is never hidden under the keyboard.

TC-BTN-008Icon buttons have an accessible name

Preconditions

  • VoiceOver / TalkBack is on

Steps

  1. Swipe to the Share icon button
  2. Swipe to the Close (×) icon button

Expected result

They are announced as "Share, button" and "Close, button", not just "button".

Tip: click a test case to expand it.

Accessibility

Buttons are where many users first get stuck. Check these:

  • Role and name: Screen readersScreen readerSoftware that reads the screen aloud for blind and low-vision users, like VoiceOver or TalkBack.Open in glossary → must announce the label and the word “button”. A clickable div has no role, can’t be focused with Tab, and ignores Enter and Space.
    Dev note
    On the web, use a real <button> element.
  • Icon-only buttons need an accessible name.
    Dev note
    accessibilityLabel on iOS, contentDescription on Android, aria-label on the web.
  • Disabled and busy states must be announced: “dimmed” on iOS, “disabled” on Android and the web. While loading, a live message (“Processing payment”) tells the user something is happening.
    Dev note
    Or aria-busy="true".
  • Touch targetsTouch targetThe area of the screen that responds to a tap, which should be large enough for a finger.Open in glossary →: at least 44×44 pt on iOS and 48×48 dp on Android. On the web, WCAG 2.2 asks for at least 24×24 CSS px (2.5.8, level AA); 44×44 CSS px (2.5.5, level AAA) is the comfortable target.
  • Contrast: label text at least 4.5:1 against the button, and the button’s shape at least 3:1 against the background. Disabled buttons are exempt, but they must still be recognizable.
  • Focus: on the web, every button is reachable with Tab, has a visible focus ring, and works with both Enter and Space.
  • Don’t rely on color alone for destructive actions. Red plus a clear label (“Delete account”) works for color-blind users too.

Mobile vs web

Dev note
Native: UIButton with UIButton.Configuration (filled, tinted, gray, plain) or SwiftUI Button with .borderedProminent, .bordered, .borderless.
  • Pressed state is a quick dim/highlight, not a ripple.
  • Destructive actions are red, often inside an action sheet or alert.
    Dev note
    They use role: .destructive.
  • No FAB: the main action usually lives in the navigation bar or a bottom toolbar.
  • Test with Dynamic Type, VoiceOver, and on small screens like iPhone SE where sticky buttons get tight.
  • Use <button> for actions and <a href> for navigation. Inspect the element in DevToolsDevToolsDeveloper tools built into browsers for inspecting pages, network traffic and errors.Open in glossary → to check.
  • A button inside a form submits it by default, which causes surprise submits.
    Dev note
    type="submit" vs type="button" matters.
  • Test hover, focus and active styles, and pressing Enter in a text field (does it submit twice?).
  • Check the button on mobile browsers too: sticky bars vs the browser’s own toolbar and the on-screen keyboard.
  • Use DevTools network throttling to test the loading state and double clicks.

Check your understanding

Quick quiz

0/5 answered · 0 correct

  1. 1.A user double-taps "Send €50" on a slow connection and Anna receives €100. What should the tester report?

  2. 2.Which label is best for the confirm button in a "Remove this card?" dialog?

  3. 3.A "Terms of service" control opens a new page. What should it be?

  4. 4.After a server error, the "Pay" button keeps spinning forever. Which state was not handled?

  5. 5.What is the minimum touch target for a button on Android?

Key takeaways

  • A button does something; a link goes somewhere. Pick the right one, and use one primary button per screen.
  • Every button has six states: default, hover, pressed, focused, disabled and loading. Test all of them, in light and dark mode.
  • The costliest bugs are double submits, endless spinners and disabled buttons that still work. Test with slow and failing networks.
  • Labels should be specific verbs that fit at every text size and in every language.
  • Check touch targets (44 pt / 48 dp), confirmation for destructive actions, and screen reader announcements.

Finished this page?Nice work, this page is complete!

Mark it as done to track your progress. It's saved in this browser, no account needed.

Its review cards are now in Bites ⚡
Esc

Type to search the whole site.