Description
Use this document when you need to add or redesign tooltips in a Jetpack Compose application targeting Android large screens and desktop environments. Do not use Compose Multiplatform Desktop TooltipArea.
Expected output:
- Brief analysis of targets and input methods, followed by an implementation that preserves accessibility, focus, and design-system rules.
Workflow
Step 1: Identify tooltip targets and text
Search the code and write down every candidate before implementing. Decide targets and wording yourself.
- Icon-only controls:
IconButton,IconToggleButton,FloatingActionButton,TopAppBarnavigationIconandactions, toolbar and toolbox icons, aclickableIconwith acontentDescription, and navigation items (NavigationBarItem,NavigationRailItem,NavigationDrawerItem,navigationSuiteItems,Tab) whose label is hidden, likealwaysShowLabel = falseor a collapsed rail. - Unclear toolbar actions and controls with hidden keyboard shortcuts.
- Skip controls that are already clear from visible text. Never use a tooltip as the only source of a label, instruction, validation error, or disabled-state requirement.
- Check whether long-press already starts dragging, selection, or a context menu. Skip the tooltip if it conflicts.
For each tooltip, use a localized string and brief, sentence-case text:
- Start from the control's accessibility label: for an icon-only control the
Icon'scontentDescriptionis normally the right tooltip text. Reuse the same string resource for both so they cannot drift, and keep thecontentDescriptionon the icon — a tooltip never replaces it. - Prefer verbs for actions:
Add column,Export CSV,Mute track. - Keep plain tooltips short and clear.
- Do not repeat visible button text unless the tooltip adds a shortcut or consequence.
- Add shortcut hints only when accurate, e.g.,
Save (Ctrl+S). - For disabled controls, explain the requirement:
Select a row first.
Step 2: Implement the tooltips
Iterate through the identified targets and implement tooltips for each according to the following patterns:
- Reuse existing components: Use a shared wrapper for repeated tooltips
and inline
TooltipBoxfor one-off tooltips. - Tooltip type: Use
PlainTooltipfor a short label. UseRichTooltipfor extra details or one action. Tooltip trigger: Use the default hover-effect, long-press, and keyboard-focus behavior unless the tooltip should only be shown from code.
API:
TooltipBox(positionProvider, tooltip = { PlainTooltip { Text(…) } }, state = rememberTooltipState()) { control }. Check the Material 3 version in the Gradle files first: on 1.4.0 or newer useTooltipDefaults.rememberTooltipPositionProvider(positioning = TooltipAnchorPosition.Above); on 1.3.x that function does not exist, useTooltipDefaults.rememberPlainTooltipPositionProvider().
Follow these rules:
- Give each
TooltipBoxits ownTooltipState. - Wrap exactly one control. Keep its click, focus, enabled state, and accessibility on the control, and keep the wrapper bounds tight.
- For a rich tooltip with an action, use
rememberTooltipState(isPersistent = true), sethasAction = true, and dismiss it after the action. - Before finishing, re-run the searches from Step 1: every match now has a tooltip or a stated reason to skip it. Do not stop at the first screen.
Show tooltips from code (optional)
Use this only for brief, nonessential help, such as feature education.
- Call
tooltipState.show()from a coroutine. - Call
tooltipState.dismiss()directly when the tooltip is no longer needed. - Set
enableUserInput = falseif hover-effect, long-press, and keyboard focus should not show it. - Trigger
show()from a user event or a one-time effect, not directly during composition.
Reference links
Consult these references before proceeding with the implementation.
- AndroidX Material 3 tooltip samples
- Material Design 3 tooltip guidelines