Skip to content
AF
Accessibility

What to call an exercise, a set and a rep range

Last verified August 22, 2026 · 7 min read

A screen reader reads the string you wrote, not the layout that gave it meaning, so 3x10 at 60kg arrives as characters rather than three sets of ten reps at sixty kilograms. Google's principles page states that users must be able to understand the content and purpose of each interactive and meaningful UI element within your app, and Apple describes VoiceOver as a screen reader that lets people experience your app's interface without needing to see the screen. Neither company writes your strings, so the work is editorial: expand the gym shorthand, name the unit once, put the exercise first, and read numbers as quantities rather than glyphs. Completion belongs in state rather than the name, and Google documents a state description as describing how the On state should be referenced, noting it can be made more specific based on the context. Group the row first, since Google documents merging descendants' semantics properties as the way to make related elements one unit, then write the sentence that one unit should say.

3x10 @ 60kg is a perfectly good string for a person who can see it. It is compact because the layout carries half the meaning: the position of the row says which exercise, the column says which set, the symbol says the unit. Read aloud, it is a number, a letter, a number, a symbol, a number and two letters. Nothing in that says three sets of ten repetitions at sixty kilograms, and nothing in the platform is going to work it out for you.

This is the part of accessibility work that no API does. The platforms give you somewhere to put a string; the string is yours. This page is engineering guidance and makes no legal claims; law and store policy live in compliance.

What the platforms actually ask for#

Google's accessibility principles set the bar in one sentence: "Users must be able to understand the content and purpose of each interactive and meaningful UI element within your app." Note what it asks for — content and purpose. A weight value has content. A plus button has purpose. A set row has both.

Apple's accessibility guidelines frame the same job differently: "Describe your app's interface and content for VoiceOver. VoiceOver is a screen reader that lets people experience your app's interface without needing to see the screen." Without needing to see the screen is the test to apply to every string below. If understanding it requires knowing what column it was in, it fails.

Both companies stop there. Neither tells you how to word a superset or whether to say kilograms every time. Everything from here on is our editorial judgement, and you should overrule it where your users' vocabulary differs.

Six rules for a fitness string#

Expand the notation. Gym shorthand is a visual convention. 3x10, 8/12, @, BB, AMRAP and 1RM have to be spelled out in whatever words your product means by them — your product's meaning, not a general one you assume.

Name the unit once, where the unit lives. Say kilograms or pounds on the weight, not on every fragment of the row. If your app lets people switch units, the string is generated, never hard-coded.

Put the identity first. A listener often hears the first two words and moves on. "Bench press, set two of four" is scannable. "Set two of four, bench press" is not.

Numbers should be read as quantities, not glyphs. 1:30 is a colon between two numbers until you write it as one minute thirty seconds. +2.5 is a plus sign until you write it as increase by two point five kilograms.

Say the same thing the same way. One template per element type across the whole app. A user who has heard forty set rows should be able to predict the shape of the forty-first and listen only for the number that changed.

Do not read decoration. A rest-day dot, a divider, a chevron and a gradient are not information. If a control is icon-only, describe the action; if a graphic is decorative, it does not need a description at all.

The table#

The left column is what a fitness app renders, the middle is what we would write instead, and the right is the reason — which matters more than the example, because your wording will differ.

On screenRead literallyBetter stringWhy
3 x 10three x ten3 sets of 10 repsThe multiplication sign is a layout convention, not a word
@ 60kgat sixty k g60 kilogramsUnit spoken in full, once, on the value that carries it
8/12 on a live seteight slash twelve8 of 12 repsA slash is not a preposition
1:30 restone colon thirty1 minute 30 seconds restDuration is a quantity, not two numbers
on a finished setcheck mark, or silencelabel the set, mark it completed as a stateCompletion is a state, not part of the name
+2.5 stepperplus two point fiveincrease weight by 2.5 kilogramsPurpose, not glyph
Next buttonnextnext exercise, goblet squatWhere a control leads is part of its purpose
RPE 8R P E eighteffort 8 out of 10Spell out whatever scale you defined; see what RPE is
Superset bracketnothingsuperset, 2 exercises, bench press and rowA bracket is pure layout and is silent by default
Rep-count overlaynothingthe current count, as a value that is polledIt changes several times a second; see rep counting

The last row is different in kind: a value that changes constantly is not the same problem as a label that never changes. Apple's own documentation distinguishes an element's label from its value in exactly that context, describing the frequently-updating case as an element that "frequently updates its label or value".

A completed set is a state, not a name#

The most common labelling mistake in a training app is renaming a row when its status changes: "set 2" becomes "set 2 completed", and now the row's identity moves under the user.

Google's Compose semantics documentation has the concept for this. Of the state description, Google writes: "The StateDescription describes how the 'On' state should be referenced. By default this is a localized version of the word 'On', but this can be made more specific (for example, 'Enabled') based on the context."

Your context is training, so your states are not "on". Ours would be completed, skipped, in progress and not started. The row's name stays "set 2, 10 reps at 60 kilograms" from beginning to end; only the state changes. Two benefits: the user hears the state without re-hearing the whole row, and your logging code stops building strings by concatenation.

Group the row before you word it#

Wording a set row well does not help if the row is four separate stops. Google documents the grouping mechanism — "Composables and modifiers can indicate that they want to merge their descendants' semantics properties by calling Modifier.semantics (mergeDescendants = true) {}" — and gives the reason on the principles page: "Consolidating related elements helps users of assistive technology discover the information on the screen more efficiently."

Grouping and wording are the same decision made twice: decide what one stop is, then write the sentence it should say. Our boundary rule is one stop per decision the user makes. An exercise in a plan is one decision, a set is one decision, and a stepper is two actions that stay separate.

Where the strings come from#

Almost every string above should be generated from the same data that renders the visual row. Hand-written descriptions go stale the day somebody adds pounds, changes the rep scheme or renames the exercise, and nobody notices because nobody on the team is listening to the app.

So build one function per row type that takes the model and returns the sentence, use it for the accessible description, and test it as a pure function against the ugly cases: zero reps, a bodyweight set with no load, a range like eight to twelve, a set with no target, a unit switch mid-session. That is ordinary unit testing and belongs in your suite alongside the rest of test. The domain shapes those strings must survive are in building a strength training app and adding rep counting.

Monday morning#

Pick your busiest row type — the set row — and write down, in words, what one instance should say from cold, with no layout and no column headers. Find where that row is described in code and replace what is there with a sentence generated from the model. Move completion out of the name and into a state. Do the same for the exercise card and the rest timer, then grep for descriptions that are string literals: each one is a label that will be wrong by the next release.

Frequently asked questions

How should a screen reader read a set row like 3x10 at 60kg?
As a sentence, not as the characters. Our wording would be three sets of ten reps at sixty kilograms, generated from the same model that renders the row rather than typed as a literal. The reasoning comes from what both platforms ask for: Google states that users must be able to understand the content and purpose of each interactive and meaningful UI element within your app, and Apple describes VoiceOver as a screen reader that lets people experience an interface without needing to see the screen. A string that only makes sense in its column fails that test. The exact phrasing is editorial judgement, not a platform rule.
Should the weight unit be spoken on every set or said once per exercise?
Our judgement is once, on the value that carries it, and never as a suffix repeated across fragments of the same row. Hearing kilograms four times in one row costs the listener time and hides the number that changed. This is a wording decision rather than a platform behaviour: neither Apple's nor Google's documentation prescribes it. What does follow from the platforms is that the string must stand alone without the layout, so if a row can be reached in isolation it needs its unit somewhere. Generate the string from the model so a unit switch cannot leave it stale.
Is a finished set's completion part of its label or its state?
Its state. Renaming the row on completion moves the row's identity under the user, who then has to re-hear the whole thing to learn one bit of information. Google documents a state description as describing how the On state should be referenced, and notes it can be made more specific based on the context, giving Enabled as an example. In a training app your context supplies better words: completed, skipped, in progress, not started. Keep the row's name constant for its whole life and let the state carry the change.

Keep reading

Elsewhere on the site

Pages that share this one’s concepts and sources, from other sections.

Next steps

Was this page useful?

Independent comparison, last reviewed August 22, 2026. Pricing, rate limits, and feature availability change often — confirm current details in each provider’s official documentation before you commit. Product and company names are trademarks of their respective owners; AIFitnessAPI is not affiliated with, endorsed by, or sponsored by any product listed here.

← All accessibility · by AIFitnessAPI