Pantrix UI

Skeleton Text

A placeholder for a paragraph: lines skeleton lines lineHeight tall, the last one shorter, each in colors where they are not the theme's. The column carries one contentDescription for all of them.

import com.pantrixui.components.elements.skeleton.PantrixSkeletonText

Examples

In place of a paragraph

At its plainest: three lines standing in for a paragraph that is loading. lines is the number of lines; the last line is shorter than the others, so the block reads as a paragraph.

The band that moves across them says something is loading; when the system asks for reduced motion the band keeps still. A screen reader says "Loading" once for the three lines.

PantrixSkeletonText(lines = 3, modifier = Modifier.fillMaxWidth())

In its own colour

colors puts the skeleton in the colours it is given, not the theme's. There are two: base is the block's own colour, highlight the band that moves across it. Here both are pale versions of the app's own purple: they show whatever the ground is. When the system asks for reduced motion the band keeps still and only base shows.

Only a colour that is set changes; one that is left out stays the theme's role.

val own = ownColors()
PantrixSkeletonText(
    lines = 3,
    modifier = Modifier.fillMaxWidth(),
    colors = SkeletonColors(base = own.color.copy(alpha = 0.35f), highlight = own.color.copy(alpha = 0.15f))
)

Line height

lineHeight is the height of each line; its default is PantrixTheme.spacing.md, 12dp. It is chosen to suit the size of the text it stands in for: 8dp at the top for small text, the default in the middle, and 20dp at the bottom for a heading. The space between the lines does not change.

PantrixSkeletonText(lines = 2, modifier = Modifier.fillMaxWidth(), lineHeight = PantrixTheme.spacing.sm)
PantrixSkeletonText(lines = 2, modifier = Modifier.fillMaxWidth())
PantrixSkeletonText(lines = 2, modifier = Modifier.fillMaxWidth(), lineHeight = 20.dp)

A row announced once

An avatar and two lines: the skeleton of a list row. If both parts said "Loading", a screen reader would say it twice for the same row.

The parts are given contentDescription = null. The row as a whole says once that it is loading, in the theme's own string: PantrixTheme.strings.loading.

val loading = PantrixTheme.strings.loading
Row(
    modifier = Modifier.fillMaxWidth().semantics(mergeDescendants = true) { contentDescription = loading },
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md),
    verticalAlignment = Alignment.CenterVertically
) {
    PantrixSkeleton(modifier = Modifier.size(40.dp), shape = PantrixTheme.shapes.full, contentDescription = null)
    PantrixSkeletonText(lines = 2, modifier = Modifier.weight(1f), contentDescription = null)
}

On a fill

A skeleton does not look at the content colour around it: on a fill that is not one of the theme's grounds, the caller gives its colours. The surface here is in the info role's container.

base is a pale version of the colour written on that surface (onContainer), and highlight is the surface's own colour: the band looks like an opening that passes through the lines.

val info = PantrixTheme.colors.info
PantrixSurface(modifier = Modifier.fillMaxWidth(), color = info.container) {
    PantrixSkeletonText(
        lines = 2,
        modifier = Modifier.fillMaxWidth().padding(PantrixTheme.spacing.lg),
        colors = SkeletonColors(base = info.onContainer.copy(alpha = 0.3f), highlight = info.container)
    )
}

Until the content arrives

A skeleton resembles the content it stands in for: a circle of the same size in place of the avatar, two lines in place of two lines of text. Press the button: the content arrives and takes the skeleton's place. The caller decides which of the two is drawn.

While it loads, the circle has no description; the lines beside it say "Loading" once. The lines' tag (tagKey) is what a test waits on: once SkeletonText_profile is gone, the content has arrived.

var loading by rememberSaveable { mutableStateOf(true) }
Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md),
    verticalAlignment = Alignment.CenterVertically
) {
    if (loading) {
        PantrixSkeleton(
            modifier = Modifier.size(40.dp),
            shape = PantrixTheme.shapes.full,
            contentDescription = null
        )
        PantrixSkeletonText(lines = 2, modifier = Modifier.weight(1f), tagKey = "profile")
    } else {
        PantrixAvatar(contentDescription = null, initials = "AY", size = 40.dp)
        Column(modifier = Modifier.weight(1f)) {
            PantrixText(text = "Ayşe Yılmaz", tagKey = "profile_name")
            PantrixText(
                text = "Design team",
                style = PantrixTheme.typography.footnote,
                color = PantrixTheme.colors.textSecondary
            )
        }
    }
}
PantrixButton(
    text = if (loading) "Bring in the content" else "Reload",
    onClick = { loading = !loading },
    variant = ButtonVariant.Outline,
    size = ButtonSize.Small,
    tagKey = "toggle_profile"
)

API

lines

A placeholder for a paragraph: lines skeleton lines lineHeight tall, the last one shorter, each in colors where they are not the theme's. The column carries one contentDescription for all of them.

ParameterTypeDefaultWhat it is
linesInt
modifierModifierModifierthe column's modifier: its width, above all.
lineHeightDpPantrixTheme.spacing.md
colorsSkeletonColors?null
contentDescriptionString?PantrixTheme.strings.loading
tagKeyString?nullthe column's automation key: it is SkeletonText_<tagKey>, and its lines have no tags of their own. Without one it has no tag.

SkeletonColors

What PantrixSkeleton and PantrixSkeletonText are painted with, for a placeholder that does not take the theme's: a colour that is set is used, and one that is left out stays the theme's role.

ParameterTypeDefaultWhat it is
baseColorColor.UnspecifiedThe block's own colour, and all of it under reduced motion. The theme's disabledContent.
highlightColorColor.UnspecifiedThe lighter band that moves across the block. The theme's divider.

Accessibility