Skip to content

Tooltips

components specs/components/tooltips.kmd

Brief contextual label revealing the name or description of a UI element on hover, focus, or long-press. Material parity (`/components/tooltips`). Covers plain tooltips (label-only) and rich tooltips (multi-line + optional action).

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Tooltips

Facet Visual of Koder Design. Material parity: https://m3.material.io/components/tooltips.

2 variants

VariantUseContent
PlainIcon button label, short hint1-3 word label
RichHelp / explanation, longer contextTitle + body + optional action

Pick plain by default. Rich tooltip is opt-in for help / docs.

Anatomy (plain)

   ┌──────────┐
   │  Settings │   ← tooltip
   └──────┬───┘
          ▼      ← optional arrow (Material 3 default has no arrow)
        ┌───┐
        │ ⚙ │    ← trigger (icon button)
        └───┘
  • Background: inverse-surface (high contrast)
  • Text: inverse-on-surface, body-small (12/16, weight 400)
  • Padding: 8 px horizontal, 4 px vertical
  • Corner radius: 4 px
  • Elevation: 0 (no shadow — relies on contrast)
  • Max width: 200 dp; wraps at width
  • Arrow (optional): triangle pointing to trigger, 8 px base

Anatomy (rich)

   ┌─────────────────────────────────────────┐
   │  Voice search                            │  ← title
   │  ────────────────────────────────────── │
   │  Tap the mic to dictate. Hold for       │  ← body
   │  push-to-talk mode.                      │
   │                              [Learn more]│  ← optional action
   └─────────────────────────────────────────┘
                 ▲
              ┌──────┐
              │  🎤  │
              └──────┘
  • Background: surface-container (lighter / neutral, not inverse)
  • Title: title-small (14/20, weight 500), on-surface
  • Body: body-small (12/16, weight 400), on-surface-variant
  • Action: text button at bottom-right
  • Padding: 16 px
  • Corner radius: 12 px
  • Elevation: 2 dp (shadow + tonal overlay)
  • Max width: 320 dp

R1 — Trigger interactions

SurfaceTrigger to showTrigger to hide
MouseHover after 500 ms delayMouse leave OR Esc
KeyboardFocusBlur OR Esc
TouchLong-press (500 ms)Release OR tap elsewhere
ProgrammaticAPI callAPI call

Tooltips MUST be triggered by ALL three input modes — never mouse-only.

R2 — Positioning

PositionWhen
Top (above trigger)Default — won't conflict with click target
BottomWhen top would overflow viewport
Right / LeftCompact spaces (toolbar at top of screen)

Position auto-flips at runtime to stay inside viewport (within 8 px of edge).

Offset from trigger: 8 px gap (with or without arrow).

R3 — Plain tooltip rules

  • Single line preferred; wraps to 2 lines max
  • Don't repeat what's already visible (button has text "Save" → no tooltip "Save")
  • For icon-only buttons: tooltip is THE label
  • For text buttons: tooltip adds detail only when useful ("Save" tooltip "Save to local drive (Ctrl+S)")

R4 — Rich tooltip rules

  • Used for help / explanation, not basic labels
  • Stays visible while user moves into the tooltip area (hover bridge)
  • Single optional action (link, "Learn more", "Try it")
  • Can be dismissed by Esc or clicking outside
  • Useful for: feature discovery, what-this-does help, deprecated warnings with migration link

R5 — States (trigger)

Tooltip itself doesn't have states — the trigger does:

Trigger stateTooltip behavior
IdleTooltip hidden
Hover / FocusShow after delay
Active / PressedHide tooltip during press (avoids overlap)
DisabledTooltip still shows (explains WHY disabled is useful)

R6 — Timing

EventDelay
Hover-in show500 ms (Material 3 default; configurable 300-700 ms)
Hover-out hide300 ms (gives user time to move to tooltip if rich)
Long-press show500 ms (system gesture)
Focus show0 ms (immediate for keyboard)
Esc hide0 ms (immediate)

R7 — Multiple tooltips on same surface

  • Only ONE tooltip visible at a time
  • Moving to another trigger: previous hides immediately; new one shows after its delay
  • Long horizontal toolbar: skip the show-delay AFTER first tooltip appears (subsequent hovers within 500 ms are "instant" — feels responsive)

R8 — Accessibility

  • Plain tooltip's content goes into trigger's accessible name via aria-label OR aria-describedby pointing to the tooltip element
  • Rich tooltip: role="tooltip" on tooltip container; trigger has aria-describedby pointing to it
  • Tooltip is hidden from focus order (cannot be tabbed to) UNLESS rich tooltip has an action — then the action is part of focus order while tooltip is open
  • Esc closes any tooltip
  • Screen reader announces tooltip content when trigger receives focus
  • Don't put critical information ONLY in tooltips (must be available to all users; tooltip is supplemental)

R9 — Animation

  • Enter: fade-in + scale 0.95 → 1 from anchor edge (motion-fast, ~150 ms)
  • Exit: fade-out (motion-fast)
  • Position flip during scroll: smooth re-anchor
  • Reduced motion: instant in / out; no scale

R10 — Density

DensityPlain paddingPlain fontRich padding
Compact6 px / 3 px11 px12 px
Default8 px / 4 px12 px16 px
Comfortable12 px / 6 px14 px20 px

R11 — Per-preset variation

PresetPlainRich
material3Inverse surface, no arrowSurface-container, 12 px radius, optional action
material2Dark gray, optional arrowSolid white card with shadow
ios_cupertinoToast-style, light blurPopover style with arrow
gnomeAdwaita compact roundedAdwaita popover style
windows_11Compact pill, no arrowFluent flyout
brutalistSolid black box, sharp corners, white textSharp card with 2 px border
terminal_classic(?) suffix expands inlineBracketed help block below trigger

R12 — Forbidden patterns

  • ❌ Tooltip-only labels for critical actions (must have visible label or icon recognized by screen readers)
  • ❌ Tooltips longer than 3 lines (use rich tooltip; if rich, escalate to inline help or dialog)
  • ❌ Tooltip on disabled element WITHOUT explaining why
  • ❌ Multiple tooltips visible at once (always ≤ 1)
  • ❌ Tooltips with interactive content other than a single action (use popover or dialog)
  • ❌ Tooltips that show on click (use popover for click-triggered)
  • ❌ Tooltips that obstruct the trigger element
  • ❌ Tooltips that appear without the user having interacted at all (intrusive — use banners for proactive info)
  • ❌ Hover-only tooltips on touch surfaces (long-press fallback mandatory)
  • themes/color-roles.kmdinverse-surface (plain) / surface-container (rich)
  • themes/elevation.kmd — rich tooltip 2 dp recipe
  • themes/typography.kmdbody-small plain / title-small rich
  • themes/motion.kmd — fade-in / fade-out timing
  • interaction/states.kmd — trigger hover / focused / pressed conflicts with tooltip visibility
  • foundations/elements.kmd — Marker family

References