--- title: Tooltip description: A non-interactive popup component that displays contextual information when an element is focused, hovered, or long-pressed. Comes with tons of customization options such as animation support, arrow (caret) support and handle screen reader announcements out of the box. --- ```compose id="tooltip-demo" height=200 import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.scaleIn import androidx.compose.animation.slideInVertically import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.border import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import com.composeunstyled.Button import com.composeunstyled.RelativeAlignment import com.composeunstyled.Text import com.composeunstyled.Tooltip import com.composeunstyled.TooltipArrowDirection import com.composeunstyled.TooltipPanel import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.Canvas import androidx.compose.ui.graphics.Path COMPOSE { @Composable fun ArrowUp(modifier: Modifier = Modifier, color: Color) { Canvas(modifier = modifier.size(8.dp, 4.dp)) { val path = Path().apply { moveTo(size.width / 2f, 0f) lineTo(0f, size.height) lineTo(size.width, size.height) close() } drawPath(path, color = color) } } Tooltip( placement = RelativeAlignment.TopCenter, panel = { TooltipPanel( modifier = Modifier.zIndex(15f), enter = slideInVertically(tween(150), initialOffsetY = { (it * 0.25).toInt() }) + scaleIn( animationSpec = tween(150), transformOrigin = TransformOrigin(0.5f, 1f), initialScale = 0.65f ) + fadeIn(tween(150)), exit = fadeOut(tween(250)), arrow = { direction -> val degrees = when (direction) { TooltipArrowDirection.Up -> 0f TooltipArrowDirection.Down -> 180f TooltipArrowDirection.Left -> 90f TooltipArrowDirection.Right -> 270f } ArrowUp(Modifier.rotate(degrees), Color.Black.copy(0.8f)) } ) { Box( modifier = Modifier .clip(RoundedCornerShape(100)) .background(Color.Black.copy(0.8f)) .padding(vertical = 8.dp, horizontal = 12.dp), ) { Text("This is a tooltip", color = Color.White) } } } ) { Button( onClick = { }, contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), backgroundColor = Color.White, shape = RoundedCornerShape(8.dp), modifier = Modifier.border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(8.dp)) ) { Text("Hover me") } } } ``` ```kotlin import com.composeunstyled.Tooltip import com.composeunstyled.TooltipPanel ``` ```kotlin @Composable fun ArrowUp(modifier: Modifier = Modifier, color: Color) { Canvas(modifier = modifier.size(8.dp, 4.dp)) { val path = Path().apply { moveTo(size.width / 2f, 0f) lineTo(0f, size.height) lineTo(size.width, size.height) close() } drawPath(path, color = color) } } @Composable fun TooltipExample() { Tooltip( placement = RelativeAlignment.TopCenter, panel = { TooltipPanel( modifier = Modifier.zIndex(15f), arrow = { direction -> // Draw your arrow pointing towards the given direction val degrees = when (direction) { TooltipArrowDirection.Up -> 0f TooltipArrowDirection.Down -> 180f TooltipArrowDirection.Left -> 90f TooltipArrowDirection.Right -> 270f } ArrowUp(Modifier.rotate(degrees), Color.Black.copy(0.8f)) } ) { Text("This is a tooltip") } } ) { Button(onClick = {}) { Text("Hover me") } } } ``` ## Basic Example The `Tooltip` component has 2 main slots: its *panel* and its *anchor*. The *anchor* contains the element to which the tooltip is anchored. When this element has focus, is hovered or long-pressed (on touch) the tooltip will be displayed. The *panel* contains one of the overloads of `TooltipPanel` which renders the content of the tooltip according to the `Tooltip`'s internal state. Tooltips are placed in the same layout as the trigger. This is different to [how Tooltips work in Compose Foundation](#unstyled-tooltip-vs-compose-foundation-tooltips), in order to prevent any focus and pointer issues. Because of this, you need to use `Modifier.zIndex()` to your *panel* to place it above other elements in the same layout. ```kotlin Tooltip( placement = RelativeAlignment.TopCenter, panel = { TooltipPanel( modifier = Modifier.zIndex(15f), enter = slideInVertically(tween(150), initialOffsetY = { (it * 0.25).toInt() }) + scaleIn( animationSpec = tween(150), transformOrigin = TransformOrigin(0.5f, 1f), initialScale = 0.65f ) + fadeIn(tween(150)), exit = fadeOut(tween(250)), arrow = { direction -> val degrees = when (direction) { TooltipArrowDirection.Up -> 0f TooltipArrowDirection.Down -> 180f TooltipArrowDirection.Left -> 90f TooltipArrowDirection.Right -> 270f } ArrowUp(Modifier.rotate(degrees), Color.Black.copy(0.8f)) } ) { Box( modifier = Modifier .clip(RoundedCornerShape(100)) .background(Color.Black.copy(0.8f)) .padding(vertical = 8.dp, horizontal = 12.dp), ) { Text("Notifications", color = Color.White) } } } ) { val interactionSource = remember { MutableInteractionSource() } Button( onClick = { }, contentPadding = PaddingValues(8.dp), shape = CircleShape, modifier = Modifier.focusRing(interactionSource, 1.dp, Color(0xFF3B82F6), CircleShape), interactionSource = interactionSource ) { Icon(Lucide.BellDot, contentDescription = null) } } ``` ## Code Examples ### Styling the Tooltip The `TooltipPanel` composable contains styling properties such as *backgroundColor*, *contentColor*, *shape* and *contentPadding* for easy styling. ```kotlin Tooltip( placement = RelativeAlignment.TopCenter, panel = { TooltipPanel( modifier = Modifier.zIndex(15f), backgroundColor = Color(0xFF1E293B), contentColor = Color.White, shape = RoundedCornerShape(8.dp), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp) ) { Text("Styled tooltip") } } ) { Button(onClick = {}) { Text("Hover me") } } ``` ### Positioning the Tooltip Pass the relative alignment you want to the `Tooltip`'s *placement* property: ```kotlin Tooltip( placement = RelativeAlignment.TopEnd, panel = { TooltipPanel { Text("This is a tooltip") } } ) { Button(onClick = {}) { Text("Hover me") } } ``` ### Tooltip with Arrow (Caret) Use the `TooltipPanel` with the *arrow* parameter. The *arrow* lambda gives you the `TooltipArrowDirection` which the arrow needs to be pointing towards. We will always place the arrow between the `anchor` and the `panel` centering it to the respective direction. ```kotlin @Composable fun ArrowUp(modifier: Modifier = Modifier, color: Color) { Canvas(modifier = modifier.size(8.dp, 4.dp)) { val path = Path().apply { moveTo(size.width / 2f, 0f) lineTo(0f, size.height) lineTo(size.width, size.height) close() } drawPath(path, color = color) } } Tooltip( placement = RelativeAlignment.BottomCenter, panel = { TooltipPanel( modifier = Modifier.zIndex(15f), arrow = { direction -> // Draw your arrow pointing towards the given direction val degrees = when (direction) { TooltipArrowDirection.Up -> 0f TooltipArrowDirection.Down -> 180f TooltipArrowDirection.Left -> 90f TooltipArrowDirection.Right -> 270f } ArrowUp(Modifier.rotate(degrees), Color.Black) } ) { Text("Tooltip with arrow") } } ) { Button(onClick = {}) { Text("Hover me") } } ``` ### Animating the Tooltip Pass the respective animation specs you want in the `TooltipPanel`'s *enter* and *exit* parameters: ```kotlin Tooltip( placement = RelativeAlignment.TopCenter, panel = { TooltipPanel( modifier = Modifier.zIndex(15f), enter = fadeIn(animationSpec = tween(300)), exit = fadeOut(animationSpec = tween(300)) ) { Text("This tooltip fades in and out") } } ) { Button(onClick = {}) { Text("Hover me") } } ``` ## Unstyled Tooltip vs Compose Foundation Tooltips Tooltips in Compose Foundation are implemented using Popups. Popups do not allow pointer events behind them. This can cause weird glitches when the Tooltip is right above the trigger and mouse overed. Unstyled Tooltips are placed in the same layout as its trigger, and do not use Popups. As a result, there are no weird focus or pointer issues. ## Keyboard Interactions | Key | Description | |----------------------------------------|-------------------------------------------| |