Main Button
CTA
The main button component with three visual variants for different emphasis levels.
Ready to use
The Button is one of the most important interactive elements in the design system. It enables users to take action — saving changes, submitting a form, or moving forward in a process. A Primary Button can appear two ways: as a button (the action happens on the same page) or as a link styled as a button (the action takes the user somewhere else). The Tertiary variant carries an optional underline toggled via the boolean Underline property, keeping behaviour and appearance consistent between Figma and code.
Anatomy
Section titled “Anatomy”- Button
- Icon
- Label
When to use it
Section titled “When to use it”Use a Button when:
- The action happens on the same page (save, add, continue, open).
- The action is tied directly to the user’s current goal (Submit, Save changes, Buy now).
Use a Link styled as a Button when:
- The action takes the user to another page or external website.
Avoid a Button when:
- The interface already shows a Primary Button — only one should be visible per screen or section.
- The control is icon-only with no label — Buttons always carry text so the action is unambiguous.
Properties
Section titled “Properties”Type
Three types — Primary, Secondary, Tertiary — set the visual weight. Primary marks the single most important action on a screen; Secondary supports it; Tertiary is reserved for low-priority actions and footnote-style controls.
Size
Three sizes — Large (50px), Medium (38px), Small (32px). Large is the default and covers the majority of layouts. Small is reserved for secondary navigation bars and other dense contexts. Medium exists for legacy parity; avoid introducing new usages.
Contrast
Switches between Neutral (default — on light scheme surfaces) and Contrast (on dark or media-backed surfaces). Each contrast variant pulls its own fill / foreground tokens so legibility holds against the surface beneath.
State
Rested, Hover, Active, Disabled, Focus — each pulls its own design-token set. The Focus ring is the global focus token, not a Button-specific value, so it stays consistent with every other interactive control.
Icon
Optional leading and / or trailing icon, toggled via the Has icon left and Has icon right boolean properties. Icon-only buttons are not supported — use a Media Button instead.
Underline (Tertiary only)
Boolean that adds or removes the bottom border under the label. On by default for text-based or inline actions; off for dense layouts where the line adds visual noise.
Platform considerations
Section titled “Platform considerations”Desktop
Place the Primary Button where users naturally land — bottom-right of forms, footer of cards, or as a sticky header action. Buttons keep the same height, padding, and typography across all desktop breakpoints; only the surrounding layout adapts.
Tablet
Identical to desktop. Watch the 44×44 px touch target — use Large size whenever a button doubles as a tap target.
Mobile
Promote the Primary Button to full-width inside form layouts so the touch target stays comfortable. Sticky bottom-bar placement is acceptable when the action is mission-critical for the screen.
Best practices
Section titled “Best practices”A button is the user’s contract with the interface. Make the next step obvious; never make them guess.
Do
Keep one Primary Button per view. Lead labels with a clear, actionable verb (“Save changes”, “View details”). Group Primary + Secondary actions thoughtfully — Secondary supports the Primary, never competes with it. Always wire focus and contrast tokens through the design system.
Don't
Don’t ship icon-only buttons or vague labels (“Click here”, “OK”). Don’t stack multiple Primaries of equal weight. Don’t place Buttons on backgrounds that fight their fill / border — use Contrast variants on dark surfaces. Don’t substitute a Tertiary for a Primary action; the hierarchy matters.
Content guidelines
Section titled “Content guidelines”Lead with an actionable verb. Keep labels 1–3 words, around 24 characters in English, with 25–40% extra width budgeted for languages like German or Finnish. Avoid jargon and idioms — plain English translates cleanly. In modal groups, both buttons should be self-explanatory: “Save draft” / “Discard draft” beats “Yes” / “No”. For icon-only buttons (which only appear in the Media Button family, not here), wire an aria-label; for icon + text, don’t restate the icon’s meaning in the text.
Styles
Section titled “Styles”The main button component with three visual variants for different emphasis levels.
<button class="tng-button">Button</button><a class="tng-button" href="#">Button</a>Variants
Section titled “Variants”The default variant is primary.
<button class="tng-button is-primary">Primary</button><button class="tng-button is-secondary">Secondary</button><button class="tng-button is-tertiary">Tertiary</button><button class="tng-button is-tertiary is-ghost"> Tertiary Ghost</button>The default size is lg.
<button class="tng-button is-sm">Small</button><button class="tng-button is-md">Medium</button><button class="tng-button is-lg">Large</button>States
Section titled “States”<button class="tng-button is-primary" disabled>Primary</button><button class="tng-button is-secondary" disabled>Secondary</button><button class="tng-button is-tertiary" disabled>Tertiary</button><button class="tng-button is-tertiary is-ghost" disabled> Tertiary Ghost</button><button class="tng-button is-primary"> <i class="tng-icon icon-download" aria-hidden="true"></i> <span>Download</span></button><button class="tng-button is-secondary"> <span>Save</span> <i class="tng-icon icon-bookmark" aria-hidden="true"></i></button><button class="tng-button is-tertiary"> <i class="tng-icon icon-external-link" aria-hidden="true"></i> <span>External Link</span></button>Overflow
Section titled “Overflow”You can set overflow limitation on a button just like any other element by using the overflow utility class.
<button class="tng-button"> <div class="tng-overflow-ellipsis">…</div></button>Description
Section titled “Description”The Button is one of the most important interactive elements in the design system. It enables users to take action — for example, saving changes, submitting a form, or moving forward in a process.
There are two main ways a Primary Button can appear:
- As a Button: used when the action happens within the same page (e.g. opening a modal, submitting data).
- As a Link styled as a Button: used when the action takes the user to another page or an external website.
The Tertiary Button includes an optional underline that can be toggled on or off through the boolean property “isGhost”. This ensures consistent behaviour and appearance while simplifying maintenance and documentation across Figma and development.
Component
Section titled “Component”Properties
Section titled “Properties”ButtonProperties extends <React.ButtonHTMLAttributes<HTMLButtonElement>> which means it includes all standard HTML attributes that can be applied to a button element.
| Prop | Type | Description | Required |
|---|---|---|---|
text |
string |
The text label displayed inside the pill button | ✅ |
buttonType |
'button' | 'link' |
Defines the type of node to render | ✅ |
buttonStyle |
'primary' | 'secondary' | 'tertiary' |
The button style, 'primary' by default |
|
buttonSize |
'sm' | 'md' | 'lg' |
Defines the size of the button, 'md' by default |
|
trailingIcon |
ToyotaIcon | LexusIcon |
Icon displayed after the text. Rendered with rtlFlip on by default |
|
leadingIcon |
ToyotaIcon | LexusIcon |
Icon displayed before the text. Rendered with rtlFlip on by default |
|
additionalIconProps |
Partial<IconProperties> |
Extra props forwarded to the underlying leading/trailing Icon (e.g. to override rtlFlip or pass a custom className) |
|
isGhost |
boolean |
Indicates if the tertiary button has the underline style | |
className |
ClassValue |
Custom class names applied to the button element |
Example
Section titled “Example”import { Button } from '@tmedxp/react-components';
const ButtonExample = () => { return <Button buttonType="button" text="Main Button" />;};
export { ButtonExample };React AEM
Section titled “React AEM”Description
Section titled “Description”The Button AEM component is designed to be used only within AEM. It uses properties that come directly from the dialog options in the authoring interface. It handles styling possibilities and accessibility features based on the button type and link behavior (external, overlay, etc.).
The regular label comes from the link model.
Component
Section titled “Component”Properties
Section titled “Properties”| Prop | Type | Description | Required |
|---|---|---|---|
linkModel |
LinkModel |
Configuration object for the button’s link behavior | ✅ |
styleIds |
StyleId[] |
Array of style IDs to determine button appearance. These values come from the styling choice in the author (primary, secondary, tertiary) | |
id |
string |
Unique identifier for the button element | |
mobileLabel |
string |
Alternative text label for mobile devices (≤576px) | |
accessibilityLabel |
string |
Custom ARIA label for screen readers | |
opensInNewWindowLabel |
string |
Screen reader text for links that open in new windows. This label should come from i18n |
Example
Section titled “Example”import type { AEMComponentProperties } from '@dcx-be/aem-react-components';import { ButtonAem } from '@tmedxp/aem-react-components/atoms/button';
import type { ButtonModel } from './types/button.model';
const Button = ({ model }: AEMComponentProperties<ButtonModel>) => { return ( <ButtonAem linkModel={model.linkModel} styleIds={model.styleIds} id={model.id} mobileLabel={model.mobileLabel} accessibilityLabel={model.accessibilityLabel} opensInNewWindowLabel={'a11yOpensInNewWindow'} /> );};
export { Button };The main button is the primary call to action — a native <button> when the action happens in place, or an <a> styled as a button when it navigates elsewhere. It follows the WAI-ARIA Button pattern and meets WCAG 2.1 AA. An accessible button hinges on three things: a clear text label, the correct native semantics, and state that assistive technology can read — so its purpose and status are clear whether the user sees it, hears it, or tabs to it.
For designers
Section titled “For designers”- All button states — rested, hover, active, disabled, focus — must maintain accessible contrast on every supported surface, in both light and dark contexts. Source: WCAG 1.4.3 Contrast (Minimum).
- Hover, focus, and pressed states must be distinguished by more than colour alone — pair the colour change with a border, shadow, or icon / text emphasis. Source: WCAG 1.4.1 Use of Color.
- Focus styles must stay visible, meet contrast requirements on every supported surface, and be clearly distinguishable from hover; never remove the browser’s default outline without an accessible replacement. Source: WCAG 2.4.7 Focus Visible.
- A disabled button may sit below the 4.5:1 text-contrast threshold because it is non-interactive, but it must stay visually recognisable — aim for a clear visual separation from its enabled state. Source: WCAG 1.4.3 Contrast (Minimum).
- Use semantic colour and focus tokens (e.g.
foreground/neutral/defaultand the global focus token) rather than fixed values, so the button adapts correctly across themes and surfaces. - Keep the interactive target at least 44 × 44 px for comfortable touch use, with 24 × 24 px the absolute floor. When the visual button is smaller, add invisible padding or spacing to reach it — the same rule across all sizes (SM, MD, LG). Source: WCAG 2.5.8 Target Size (Minimum).
For developers
Section titled “For developers”Use the native <button> element for actions — submitting, toggling, opening a dialog — and an <a> for navigation. Both take .tng-button styling, but only <button> carries the implicit button role and responds to Space; a <div onclick> has neither a role nor keyboard operation, and <a href="#"> announces as a link — so neither is an acceptable substitute. A native <button> is reached with Tab and activated with Enter or Space for free.
The accessible name comes from the button’s text, so every main button carries a visible label (icon-only controls live in the Media Button family) — mark decorative icons aria-hidden="true" so they stay out of the accessibility tree. Toggle, disclosure, and disabled states each have their own wiring: prefer the native disabled attribute over aria-disabled, and reach for aria-pressed or aria-expanded only when the button is a toggle or a disclosure. The per-element roles, names, and states are in the Labelling elements section below.
Labelling elements
Section titled “Labelling elements”Give every element the role, name, and state assistive technology needs.
Button
A native <button> carries the implicit role="button"; keep an <a> that navigates as a real link rather than bolting on role="button", so it announces correctly. The accessible name comes from the button’s text content — keep that label clear and action-led, and mark any icon aria-hidden="true" so it doesn’t double the announcement.
Toggle state
For an on / off toggle button, set aria-pressed="true" / "false" and keep it in sync with the pressed state, so assistive technology announces the button as pressed or not pressed.
Disclosure state
When the button shows or hides a region — a menu, panel, or dialog — set aria-expanded="true" / "false" to reflect whether that region is currently open.
Disabled
Prefer the native disabled attribute: it removes the button from the tab order and communicates the state automatically. Only when a disabled button must stay focusable — to surface a tooltip explaining why — use aria-disabled="true" instead, and block activation in JavaScript.
Source: WCAG 4.1.2 Name, Role, Value.