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”-
Container — fill, border, and corner radius
- Label
-
Optional leading icon
-
Optional trailing icon
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 | Optional |
|---|---|---|---|
text | string | The text label displayed inside the pill button | ❌ |
buttonStyle | 'primary' | 'secondary' | 'tertiary' | The button style, 'primary' by default | ✅ |
buttonSize | 'sm' | 'md' | 'lg' | Defines the size of the button, 'md' by default | ✅ |
buttonType | 'button' | 'link' | Defines the type of node to render | ❌ |
trailingIcon | ToyotaIcon | LexusIcon | Icon displayed after the text | ✅ |
leadingIcon | ToyotaIcon | LexusIcon | Icon displayed before the text | ✅ |
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 | Optional |
|---|---|---|---|
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 };For designers
Section titled “For designers”To ensure that all button variants meet accessibility standards across platforms, follow the implementation checklist below. These recommendations align with WCAG 2.1 AA, support keyboard navigation, and are compatible with assistive technologies.
- All button states (rested, hover, active, disabled, focus) must maintain accessible contrast in both light and dark background contexts.
- Focus styles must remain visible and meet WCAG AA contrast requirements across all supported surfaces. Focus should be clearly distinguishable from hover. Use a high-contrast outline or border (e.g. #0072F0 against adjacent backgrounds).
- Hover, focus and pressed states must be clearly differentiated using more than colour alone. Examples: colour change plus border or shadow; icon or text emphasis.
- All buttons must show a visible focus style (not just a colour change) on keyboard navigation. Never remove the browser’s default outline unless replaced with an accessible alternative.
- Disabled buttons must not be focusable and must not trigger actions. Maintain sufficient visual contrast for recognition (ideally ≥ 3:1, or provide a clear visual separation).
Minimum touch area
Section titled “Minimum touch area”- Absolute minimum: 24 × 24 px — the lowest technical requirement. It ensures the element can still be reached but does not guarantee an optimal experience.
- Recommended best practice: 44 × 44 px — follows WCAG 2.5.5 Target Size (Level AA) and EN 301549 guidance. Provides a more comfortable interaction area for users with motor difficulties, touch devices, or when using a device outdoors. Should be the default requirement.
- The minimum target size applies regardless of the visual size of the component. If the visual element is smaller, additional spacing or invisible padding must be added. Consistency is key: the same rule applies across all sizes (LG, MD, SM).
For developers
Section titled “For developers”To ensure buttons meet WCAG 2.1 AA and support assistive technologies, follow these implementation standards:
- Semantic HTML
- Always use the native
<button>element (not<div>or<a>without role). If an<a>element must be used as a button, ensurerole="button"is set, that it is focusable withtabindex="0", and that it supports Enter/Space keypress events. This should be considered a fallback only.
- Always use the native
- Keyboard Navigation
- All buttons must be reachable with Tab and activatable with Enter and Space.
- Icon-only buttons must include accessible labelling:
aria-label="Edit"oraria-labelledby="..."when referencing visible text elsewhere. - Ensure keyboard shortcuts follow platform conventions (Enter/Space on desktop, tap on mobile).
- Focus Ring / Visible Focus
- All buttons must show a visible focus style (not just a colour change) on keyboard navigation.
- Use a high-contrast outline or border (e.g. #0072F0 against adjacent backgrounds).
- Focus must remain persistent and highly visible, not replaced by hover styles.
- Never remove the browser’s default outline unless replaced with an accessible alternative.
- Screen Reader Support
- Each button must include a clear and concise text label (e.g.
<button>Edit</button>). - For icon-only buttons: use
aria-labelortitleto describe the action (e.g. “Download PDF”). - Avoid redundant roles or
aria-hidden="true"on interactive buttons.
- Each button must include a clear and concise text label (e.g.
- Disabled State
- Use the
disabledattribute on<button>rather than visually faking it. - Disabled buttons must not be focusable and must not trigger actions.
- Maintain sufficient visual contrast for recognition (ideally ≥ 3:1, or provide a clear visual separation).
- Note: contrast may fall below 4.5:1 if disabled buttons are visibly distinguishable and not interactive, as per WCAG 2.1 SC 1.4.3.
- Use the
- Interaction Feedback
- Hover, focus and pressed states must be clearly differentiated using more than colour alone.
- Examples: colour change plus border or shadow; icon or text emphasis.
- Responsive and Tap Targets
- Touch targets must meet minimum sizes (WCAG + mobile UX best practices).
- Ensure icon-only or small buttons are not too small on touch devices.
- Use spacing tokens to maintain consistency across breakpoints.
Semantic markup
Section titled “Semantic markup”- Use
<button>for actions (submit, toggle, open dialog). Use<a>for navigation. Both can be styled with.tng-button, but only<button>responds to Space and has implicitrole="button". - Never use
<a href="#">as a button substitute — screen readers announce it as a link.
States
Section titled “States”- Disabled — prefer the native
disabledattribute. It removes the button from tab order and communicates the state automatically. - Focusable disabled — if a disabled button must remain focusable (e.g. to show a tooltip), use
aria-disabled="true"instead and prevent the click in JavaScript. - Toggle buttons — use
aria-pressed="true"/"false"for on/off toggles. Usearia-expandedwhen the button controls a collapsible region.
Touch targets
Section titled “Touch targets”All button variants use the tng-touch-target utility to meet the WCAG 2.5.8 minimum target size of 44×44 CSS pixels.
Good vs bad
Section titled “Good vs bad”| Case | ✅ Good | ❌ Bad |
|---|---|---|
| Semantic Structure | <button>Save</button> | <div onclick="save()"> |
| Keyboard Navigation | Activatable using Enter / Space | Only activatable with pointer click |
| Focus Visibility | Clear, high-contrast focus ring | Focus removed or replaced by hover-only styling |
| Disabled State | <button disabled> with muted styling | Visually greyed but still interactive |
| Screen Reader | <button aria-label="Download PDF"> for icon | Icon button with no accessible name |