Current version 22.2.0

Button Beta

import { Tg2ButtonComponent } from '@trademe/tangram';

The next-generation button directive. Applies to <button> and <a> elements via the tg2Button attribute.

Styles

All our different variants of buttons are set by adding the tg2Style attribute, set to one of the following options.

default
secondary
transparent
destructive
expressive
enhanced

States

Default size · square shape. Each variant has a distinct disabled treatment.

Button states
RestingHoverFocusActiveDisabledLoading
Primary - default
Secondary
Transparent
Destructive
Expressive
Enhanced

Sizes

Button sizes
PrimarySecondaryDestructiveExpressive
Small (32px)
Medium (40px)
Large (48px)

Shapes

Button

Button shapes
SquareRound
Small (32px)
Medium (40px)
Large (48px)

Icon only

Icon only shapes
SquareRound
Small (32px)
Medium (40px)
Large (48px)

Icons

Button icons
PrimarySecondaryExpressiveDestructive
Leading
Trailing

Icon only

Two mutually exclusive inputs hide a button's label — pick based on whether it should ever be visible.

iconOnly vs iconOnlyBelow
InputLabel hiddenUse when
[iconOnly]Always, at every viewport sizeThe icon alone is always enough (e.g. a bare close or delete action)
[iconOnlyBelow] (sm or md) Only below that breakpoint — the full label shows above itThe label helps on larger screens but the button needs to shrink on mobile

The label text always stays in the DOM for screen readers. iconOnlyBelow is CSS-only, so there's no hydration flash — try it on the Code Example tab.

Square

Icon only buttons, square
PrimarySecondaryTransparentDestructiveExpressiveEnhanced
Large (48px)
Medium (40px)
Small (32px)

Round

Icon only buttons, round
PrimarySecondaryTransparentDestructiveExpressiveEnhanced
Large (48px)
Medium (40px)
Small (32px)

Conditional icon + label (toggle buttons)

Icon and label as siblings inside an @if/@else block hits an upstream Angular content-projection limitation — the icon silently lands inside the label instead of beside it.

Do
Keep <tg-icon-left> as an unconditional sibling and vary only its bindings and the text.
<button
    tg2Button
    [toggled]="isWatchlisted()"
    (click)="isWatchlisted.set(!isWatchlisted())">
    <tg-icon-left
        [name]="isWatchlisted() ? 'binoculars-tick' : 'binoculars-plus'"
        size="small"
        aria-hidden="true"></tg-icon-left>
    {{ isWatchlisted() ? 'Watchlisted' : 'Add to Watchlist' }}
</button>
Don't
Put <tg-icon-left> and the label text as siblings inside an @if/@else block.
<button
    tg2Button
    [toggled]="isWatchlisted()"
    (click)="isWatchlisted.set(!isWatchlisted())">
    @if (isWatchlisted()) {
        <tg-icon-left name="binoculars-tick" size="small" aria-hidden="true"></tg-icon-left>
        Watchlisted
    } @else {
        <tg-icon-left name="binoculars-plus" size="small" aria-hidden="true"></tg-icon-left>
        Add to Watchlist
    }
</button>

Apply tg2Button to an <a> element to style it identically to a <button>.

Link buttons
PrimarySecondaryTransparent
ButtonButtonButton
Proudly made by © 2026 Trade Me Limited