Pantrix UI

Icon Button

A button that shows only an icon: square, as tall as a size button, round by default, with the platform's minimum touch target around it. variant picks its colours; ButtonVariant.Text, the default, has no container, disabled or not. contentDescription names the action for screen readers, "Close" rather than "X".

import com.pantrixui.components.elements.iconbutton.PantrixIconButton

Examples

At its plainest

The least it takes: an icon, a description that says what it does, and the action to run when it is pressed. Its variant is Text and its size Medium: a round button of 40dp with no fill.

Press the button: the line under it says how many times it was pressed. As it has no fill, the button's circle shows while it is pressed and while the keyboard focus is on it.

var refreshed by rememberSaveable { mutableIntStateOf(0) }
PantrixIconButton(
    imageVector = PantrixIcons.Refresh,
    contentDescription = "Refresh",
    onClick = { refreshed += 1 },
    tagKey = "refresh"
)
PantrixText(
    text = "Times refreshed: $refreshed",
    color = PantrixTheme.colors.textSecondary,
    tagKey = "refreshed"
)

Variants

variant picks the button's colours; all six are PantrixButton's variants. Solid is the main action on a screen, Outline, Soft and Subtle are secondary actions, and Destructive says the action cannot be undone.

The default is Text: it has no surface, and only its icon is drawn. It is for the actions in a toolbar.

FlowRow(
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
    verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)
) {
    ButtonVariant.entries.forEach { variant ->
        PantrixIconButton(
            imageVector = PantrixIcons.Star,
            contentDescription = "Button in the ${variant.name} variant",
            onClick = {},
            variant = variant,
            tagKey = "variant_${variant.name}"
        )
    }
}

Sizes

size picks one of five sizes: the button is a square of 24, 32, 40, 48 or 56 dp, and its icon is 16, 20, 24, 28 or 32 dp. The default is Medium. Beside a PantrixButton of the same size, the two are the same height.

The room around the small buttons is the touch target: a button is drawn small but takes up as much room as the platform's minimum touch target. That is 48dp on Android, 44dp on iOS, and 32dp on desktop and the web when they are used with a mouse.

FlowRow(
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
    verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
    itemVerticalAlignment = Alignment.CenterVertically
) {
    ButtonSize.entries.forEach { size ->
        PantrixIconButton(
            imageVector = PantrixIcons.Plus,
            contentDescription = "Button in the ${size.name} size",
            onClick = {},
            variant = ButtonVariant.Soft,
            size = size,
            tagKey = "size_${size.name}"
        )
    }
}

Shape and style

An icon button is fully round on every platform. shape changes that: the second button has the theme's medium roundness (shapes.md), and the third has square corners (shapes.none).

style changes the button's dimensions: an icon button reads only the sizes of ButtonStyle. The last button is still Medium, but its icon is drawn at 16dp instead of 24dp. Only the field that is set changes, and the rest comes from the theme's style.

Row(
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
    verticalAlignment = Alignment.CenterVertically
) {
    PantrixIconButton(
        imageVector = PantrixIcons.Folder,
        contentDescription = "Fully round button",
        onClick = {},
        variant = ButtonVariant.Soft,
        tagKey = "pill"
    )
    PantrixIconButton(
        imageVector = PantrixIcons.Folder,
        contentDescription = "Slightly round button",
        onClick = {},
        variant = ButtonVariant.Soft,
        shape = PantrixTheme.shapes.md,
        tagKey = "rounded"
    )
    PantrixIconButton(
        imageVector = PantrixIcons.Folder,
        contentDescription = "Square-cornered button",
        onClick = {},
        variant = ButtonVariant.Soft,
        shape = PantrixTheme.shapes.none,
        tagKey = "square"
    )
    PantrixIconButton(
        imageVector = PantrixIcons.Folder,
        contentDescription = "Button with a small icon",
        onClick = {},
        variant = ButtonVariant.Soft,
        style = ButtonStyle(medium = ButtonSizeStyle(iconSize = 16.dp)),
        tagKey = "small_icon"
    )
}

Disabled

enabled = false draws the button faded, and the button takes no press. A variant with a fill takes the theme's disabled fill; Text and Outline, which have no fill, stay without one when disabled, and do not turn into a grey shape.

Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    listOf(ButtonVariant.Text, ButtonVariant.Outline, ButtonVariant.Soft, ButtonVariant.Solid).forEach { variant ->
        PantrixIconButton(
            imageVector = PantrixIcons.Calendar,
            contentDescription = "Add to calendar",
            onClick = {},
            variant = variant,
            enabled = false,
            tagKey = "disabled_${variant.name}"
        )
    }
}

In another role and in a colour of its own

A variant's colours come from the theme's primary role, and Destructive's from the danger role. PantrixColor.buttonColors(variant) gives the same variant in another role: the first two buttons are in the success and danger roles.

The last button's colour is in none of the theme's roles: ButtonColors takes the fill and the icon's colour directly. The icon is given no colour of its own: it takes the button's content colour by itself.

val colors = PantrixTheme.colors
val own = ownColors()
Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    PantrixIconButton(
        imageVector = PantrixIcons.Check,
        contentDescription = "Approve",
        onClick = {},
        colors = colors.success.buttonColors(ButtonVariant.Soft),
        tagKey = "approve"
    )
    PantrixIconButton(
        imageVector = PantrixIcons.Close,
        contentDescription = "Reject",
        onClick = {},
        colors = colors.danger.buttonColors(ButtonVariant.Outline),
        tagKey = "reject"
    )
    PantrixIconButton(
        imageVector = PantrixIcons.Star,
        contentDescription = "Add to collection",
        onClick = {},
        colors = ButtonColors(container = own.color, content = own.onColor),
        tagKey = "own_colors"
    )
}

Its own content: an icon with a badge

The PantrixIconButton that takes content in place of an icon: here an icon with a badge at its corner. The content is drawn in the button's content colour.

What is in the button is a picture of what the description says: a screen reader hears the description alone. So the badge's count is written in the description, "Inbox, 3 unread"; the badge itself is not read. "New message" raises the count, and a press on the icon button resets it.

var unread by rememberSaveable { mutableIntStateOf(3) }
Row(
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md),
    verticalAlignment = Alignment.CenterVertically
) {
    PantrixIconButton(
        contentDescription = if (unread > 0) "Inbox, $unread unread" else "Inbox",
        onClick = { unread = 0 },
        tagKey = "inbox"
    ) {
        PantrixBadgedBox(badge = { if (unread > 0) PantrixBadge(count = unread) }) {
            PantrixIcon(imageVector = PantrixIcons.Inbox, contentDescription = null)
        }
    }
    PantrixButton(
        text = "New message",
        onClick = { unread += 1 },
        variant = ButtonVariant.Outline,
        size = ButtonSize.Small,
        tagKey = "new_message"
    )
}

A button without a description

contentDescription = null is given only to a button that repeats what something else already does. Here the whole card is pressed and opens the order; the arrow at its end does the same thing and is a sign for the eye alone.

Such a button is hidden from screen readers and is no stop for the keyboard: a stop on something the reader will never read is worse than no stop at all. A mouse and a finger still press it. A button that is the only way to its action always has a description.

var opened by rememberSaveable { mutableIntStateOf(0) }
PantrixCard(
    modifier = Modifier.widthIn(max = 360.dp),
    variant = CardVariant.Outlined,
    onClick = { opened += 1 },
    tagKey = "order"
) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Column(modifier = Modifier.weight(1f)) {
            PantrixText(text = "Order 8F2A-19C4", style = PantrixTheme.typography.callout)
            PantrixText(text = "Arrives tomorrow", color = PantrixTheme.colors.textSecondary)
        }
        PantrixIconButton(
            imageVector = PantrixIcons.ChevronForward,
            contentDescription = null,
            onClick = { opened += 1 },
            tagKey = "open_order"
        )
    }
}
PantrixText(
    text = "Times the order was opened: $opened",
    color = PantrixTheme.colors.textSecondary,
    tagKey = "order_opened"
)

API

imageVector

A button that shows only an icon: square, as tall as a size button, round by default, with the platform's minimum touch target around it. variant picks its colours; ButtonVariant.Text, the default, has no container, disabled or not. contentDescription names the action for screen readers, "Close" rather than "X".

ParameterTypeDefaultWhat it is
imageVectorImageVectorthe icon the button shows.
contentDescriptionString?
onClick() -> Unitwhat a press does.
modifierModifierModifierthe button's modifier.
variantButtonVariantButtonVariant.Text
sizeButtonSizeButtonSize.Medium
enabledBooleantruewhether the button can be pressed: one that cannot is drawn in the disabled colours and does nothing.
shapeShapePantrixTheme.shapes.fullthe button's corners: fully round unless told.
colorsButtonColorsvariant.colors()this call's own colours, in place of variant's.
tagKeyString?nullthe button's automation key: the button is IconButton_<tagKey>, and without one a key of where it is called. Visible text is never the key.
interactionSourceMutableInteractionSource?nullthe button's interactions, hoisted: its focus, its hover and its presses. It keeps a source of its own when this is null.
styleButtonStyle?nullthis call's own style: over every enclosing one and under the density. Only the fields it sets change.

contentDescription

PantrixIconButton with any content in place of an ImageVector: an animated icon, or one with a badge.

ParameterTypeDefaultWhat it is
contentDescriptionString?
onClick() -> Unitwhat a press does.
modifierModifierModifierthe button's modifier.
variantButtonVariantButtonVariant.Textthe button's colours: ButtonVariant.Text, with no container, unless told.
sizeButtonSizeButtonSize.Mediumthe button's size: as tall, and as wide, as a size button is tall.
enabledBooleantruewhether the button can be pressed: one that cannot is drawn in the disabled colours and does nothing.
shapeShapePantrixTheme.shapes.fullthe button's corners: fully round unless told.
colorsButtonColorsvariant.colors()this call's own colours, in place of variant's.
tagKeyString?nullthe button's automation key: the button is IconButton_<tagKey>, and without one a key of where it is called. Visible text is never the key.
interactionSourceMutableInteractionSource?nullthe button's interactions, hoisted: its focus, its hover and its presses. It keeps a source of its own when this is null.
styleButtonStyle?nullthis call's own style: over every enclosing one and under the density. Only the fields it sets change.
content@Composable () -> Unit

ButtonStyle

PantrixButton's geometry: its shape, its press feedback, and a height, padding and icon size for each ButtonSize. PantrixIconButton reads the sizes only.

ParameterTypeDefaultWhat it is
shapeShapeSpecShapeSpec.UnspecifiedThe button's shape: ShapeRole.Full on Android, desktop and the web, ShapeRole.Medium on iOS.
pressedScaleFloatFloat.NaNScale while pressed. 1f means none: the state layer carries the feedback.
extraSmallButtonSizeStyleButtonSizeStyle()ButtonSize.ExtraSmall's geometry.
smallButtonSizeStyleButtonSizeStyle()ButtonSize.Small's geometry.
mediumButtonSizeStyleButtonSizeStyle()ButtonSize.Medium's geometry.
largeButtonSizeStyleButtonSizeStyle()ButtonSize.Large's geometry.
extraLargeButtonSizeStyleButtonSizeStyle()ButtonSize.ExtraLarge's geometry.

ButtonColors

What a button is painted with. ButtonVariant.colors gives a variant's in primary, and PantrixColor.buttonColors the same prominences in any role.

ParameterTypeDefaultWhat it is
containerColorThe fill; transparent for an outline or a text button.
contentColorThe label's and the icon's colour.
borderColorColor.UnspecifiedThe edge's colour, or Color.Unspecified for a button without an edge.

Accessibility