Pantrix UI

Link

A link on its own that runs onClick: text underlined in the primary colour, with the platform's minimum touch target around it. The click is reported to the theme's analytics as a Link click event. When it is not enabled, it keeps its underline, in the disabled text colour, so that it still reads as a link, and does nothing.

import com.pantrixui.components.elements.link.PantrixLink

Examples

A link that runs an action

At its plainest: a text and an onClick that runs when it is pressed. The link is written underlined, in the theme's primary colour. Press "Forgot my password": the app's snackbar lets you know.

It is given a tagKey: automation finds this link by the tag Link_forgot_password.

val snackbars = LocalPantrixSnackbars.current
val scope = rememberCoroutineScope()
PantrixLink(
    text = "Forgot my password",
    onClick = { scope.launch { snackbars?.show(message = "Reset link sent") } },
    tagKey = "forgot_password"
)

A link that opens an address

The second overload takes url in place of onClick. When it is pressed the address is opened with the platform's UriHandler: in the browser, or in the app that handles the address. Its look and its other parameters are the same as the first overload's.

PantrixLink(text = "Privacy policy", url = "https://example.com/privacy", tagKey = "privacy_policy")

In its own colour

color is the link's own colour: it sets its text, its underline, and the tint that appears under it when a pointer comes over it or presses it. The first link's colour is the app's own purple, which is in none of the theme's roles; the second's is the theme's danger role.

val snackbars = LocalPantrixSnackbars.current
val scope = rememberCoroutineScope()
val own = ownColors()
Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xl)) {
    PantrixLink(
        text = "Campaign terms",
        onClick = { scope.launch { snackbars?.show(message = "Campaign terms opened") } },
        color = own.color,
        tagKey = "campaign_terms"
    )
    PantrixLink(
        text = "Delete my account",
        onClick = {},
        color = PantrixTheme.colors.danger.color,
        tagKey = "delete_account"
    )
}

On a fill

A link does not look at the content colour around it: given no colour, it is in the theme's primary colour on a fill too. Where the fill is primary itself, that colour cannot be read; the caller gives the colour that reads there with color, here the role's onColor.

The PantrixText beside it is given no colour: it takes the fill's content colour by itself.

val primary = PantrixTheme.colors.primary
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = primary.color) {
    Row(
        modifier = Modifier.padding(horizontal = PantrixTheme.spacing.lg),
        horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm),
        verticalAlignment = Alignment.CenterVertically
    ) {
        PantrixText(text = "A new version is ready.")
        PantrixLink(text = "Learn more", onClick = {}, color = primary.onColor, tagKey = "learn_more")
    }
}

Beside a text, and its text style

A link given no style is in the theme's label style, like a PantrixText given no style: in the top row the two stand side by side, at the same size.

The link below is given style = PantrixTheme.typography.footnote: the small link under a form. Though its text is smaller, the link is never shorter than the platform's touch target; it is as wide as its text, and starts where the row above it starts.

Row(
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xs),
    verticalAlignment = Alignment.CenterVertically
) {
    PantrixText(text = "Don't have an account?")
    PantrixLink(text = "Sign up", onClick = {}, tagKey = "sign_up")
}
PantrixLink(
    text = "Terms of use",
    onClick = {},
    style = PantrixTheme.typography.footnote,
    tagKey = "terms_of_use"
)

Links in a row

Links that do not stand in a sentence: the row at a page's foot. Where they stand already says they are links; with underline = LinkUnderline.OnHover they are underlined only under a pointer and while they hold the keyboard's focus.

Where the platform is used by touch there is neither: on Android and iOS these links too are underlined always. A link inside a sentence stays at the default, LinkUnderline.Always: its colour alone does not reach whoever cannot tell it apart.

Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.lg)) {
    PantrixLink(text = "Privacy", onClick = {}, underline = LinkUnderline.OnHover, tagKey = "privacy")
    PantrixLink(text = "Terms", onClick = {}, underline = LinkUnderline.OnHover, tagKey = "terms")
    PantrixLink(text = "Contact", onClick = {}, underline = LinkUnderline.OnHover, tagKey = "contact")
}

Touch target

The area a link takes up is painted here. It is at least as tall as the platform's touch target: 48dp on Android, 44dp on iOS, and 32dp on desktop and the web when they are used with a mouse. It is as wide as its text.

The second link is in the caption style: the text is smaller, and the area stays as tall. A touch just beside a short link reaches it too.

val area = PantrixTheme.colors.neutral.container
Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xl)) {
    PantrixLink(text = "Help", onClick = {}, modifier = Modifier.background(area), tagKey = "help_body")
    PantrixLink(
        text = "Help",
        onClick = {},
        modifier = Modifier.background(area),
        style = PantrixTheme.typography.caption,
        tagKey = "help_caption"
    )
}

Disabled

enabled = false draws the link in the theme's disabled text colour, and the link takes no press. It keeps its underline: without it, it would read as plain text. Press "Resend code": the link is disabled for five seconds.

The second link is given a color as well. A disabled link is in the theme's disabled colour whatever is given.

var waiting by remember { mutableStateOf(false) }
LaunchedEffect(waiting) {
    if (waiting) {
        delay(5_000)
        waiting = false
    }
}
Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xl)) {
    PantrixLink(
        text = "Resend code",
        onClick = { waiting = true },
        enabled = !waiting,
        tagKey = "resend_code"
    )
    PantrixLink(
        text = "Coming soon",
        onClick = {},
        color = ownColors().color,
        enabled = false,
        tagKey = "coming_soon"
    )
}

Reporting a click

Every click is reported to the theme's analytics as an event: its component is Link and its action click. The event's external value says whether the link ran an action (false) or opened an address (true); the address itself is not in the event.

The two links here are inside a theme that is given only analytics: the last event to arrive is written under them. Press one.

var lastEvent by remember { mutableStateOf("No event yet") }
val analytics = remember {
    PantrixAnalytics { event ->
        val external = event.props["external"]
        lastEvent = "${event.component} · ${event.action} · ${event.testTag} · external=$external"
    }
}
PantrixTheme(analytics = analytics) {
    Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xl)) {
        PantrixLink(text = "Help", onClick = {}, tagKey = "help")
        PantrixLink(text = "Support page", url = "https://example.com/support", tagKey = "support")
    }
}
PantrixText(text = lastEvent, color = PantrixTheme.colors.textSecondary, tagKey = "link_event")

API

text

A link on its own that runs onClick: text underlined in the primary colour, with the platform's minimum touch target around it. The click is reported to the theme's analytics as a Link click event. When it is not enabled, it keeps its underline, in the disabled text colour, so that it still reads as a link, and does nothing.

ParameterTypeDefaultWhat it is
textString
onClick() -> Unit
modifierModifierModifierthe link's modifier.
styleTextStyleLocalPantrixTextStyle.current
colorColorColor.Unspecified
enabledBooleantrue
tagKeyString?nullthe link's automation key: the link is Link_<tagKey>, and without one a key of where it is called. Visible text is never the key.
underlineLinkUnderlineLinkUnderline.Alwayswhen the link is underlined: always unless told; OnHover for a link that stands apart from any sentence, underlined under a pointer and the keyboard's focus, and always where the platform is used by touch.

text

A link on its own that opens url with the platform's UriHandler: in the browser, or the app that handles the address.

ParameterTypeDefaultWhat it is
textStringwhat the link says.
urlString
modifierModifierModifierthe link's modifier.
styleTextStyleLocalPantrixTextStyle.currentthe link's type: LocalPantrixTextStyle unless given, so that a link beside a text is as large as the text.
colorColorColor.Unspecifiedthe link's own colour, where it is not the theme's primary.
enabledBooleantruewhether the link can be pressed: one that cannot keeps its underline, in the disabled text colour.
tagKeyString?nullthe link's automation key: the link is Link_<tagKey>, and without one a key of where it is called. Visible text is never the key.
underlineLinkUnderlineLinkUnderline.Alwayswhen the link is underlined, as the other PantrixLink has it.

Accessibility