You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* fix: Tooltip focus issue
1. Fixed issue when Tooltip loses its focus and disappear immediately when its content has interactive elements.
2. Fixed issue when Tooltip does not announce its content when its anchor is not an iteractive element (eg.div)
3. Added A11y section to docs.
* add new hasInteractiveContent prop
* update changelog
{/* Add padding to ensure 24x24 tooltip tap target for a11y compliance */}
84
84
<Tooltip content="This will be visible to other users.">
85
-
<Icon active color="fg" name="info" padding={0.75} size="xs" tabIndex={0} />
85
+
<Icon active color="fg" name="info" padding={0.75} size="xs" tabIndex={0} role="button"/>
86
86
</Tooltip>
87
87
</HStack>
88
88
}
@@ -111,3 +111,89 @@ function TooltipVisibilityDelay() {
111
111
);
112
112
}
113
113
```
114
+
115
+
### Accessibility (A11y)
116
+
117
+
When tooltip content is non-interactive (text only), focus stays on the trigger and the tooltip is rendered in a portal. When tooltip content has **interactive elements** (links, buttons), set **`hasInteractiveContent={true}`** so the tooltip stays in the document flow and keyboard users can tab into the content. This sets `disablePortal`, `disableAutoFocus`, and `disableFocusTrap` appropriately.
118
+
119
+
**Tooltip on an icon (or other non-button anchor):**
120
+
121
+
When using an Icon (or other non-`<button>` element) as the tooltip trigger, add **`role="button"`** and **`tabIndex={0}`** so screen readers (e.g. VoiceOver) can discover it with arrow keys and announce the tooltip. If the icon performs an action on click, use **IconButton** instead so the trigger is a real `<button>`.
122
+
123
+
**Example: tooltip on an icon (string content)**
124
+
125
+
```jsx live
126
+
functionTooltipIconStringContent() {
127
+
return (
128
+
<HStack alignItems="center" gap={2}>
129
+
<Tooltip content="This will be visible to other users.">
130
+
<Icon active color="fg" name="info" role="button" tabIndex={0} />
131
+
</Tooltip>
132
+
<Text as="span" font="body" color="fgMuted">
133
+
Focus the icon to hear the tooltip announced.
134
+
</Text>
135
+
</HStack>
136
+
);
137
+
}
138
+
```
139
+
140
+
**Example: tooltip on an icon (React node content)**
141
+
142
+
```jsx live
143
+
functionTooltipIconReactNodeContent() {
144
+
return (
145
+
<HStack alignItems="center" gap={2}>
146
+
<Tooltip
147
+
content={
148
+
<Text font="label2">
149
+
Styled <strong>description</strong> text.
150
+
</Text>
151
+
}
152
+
>
153
+
<Icon active color="fg" name="info" role="button" tabIndex={0} />
154
+
</Tooltip>
155
+
<Text as="span" font="body" color="fgMuted">
156
+
Focus the icon to hear the tooltip announced.
157
+
</Text>
158
+
</HStack>
159
+
);
160
+
}
161
+
```
162
+
163
+
**When tooltip content is interactive (links, buttons):**
164
+
165
+
- Prefer **Modal** or another pattern for actionable content when possible. Tooltips are intended for short, non-interactive descriptions.
166
+
- If you must use interactive content inside a tooltip, set **`hasInteractiveContent={true}`** so the content stays in the document flow. The prop allows keyboard users to tab into the tooltip, through its content, and out to the next focusable element on the page. With the default portal, focus behavior can be inconsistent when moving between trigger and content.
167
+
168
+
**Example: tooltip with interactive content**
169
+
170
+
```jsx live
171
+
functionTooltipWithInteractiveContent() {
172
+
return (
173
+
<Box position="relative" paddingY={5}>
174
+
<Text as="span" font="body" color="fgMuted">
175
+
Set your default display currency.{''}
176
+
</Text>
177
+
<Tooltip
178
+
content={
179
+
<Text font="label2" color="fg">
180
+
Learn more at{''}
181
+
<Text
182
+
as="a"
183
+
href="https://www.coinbase.com/settings"
184
+
target="_blank"
185
+
rel="noopener noreferrer"
186
+
>
187
+
Settings
188
+
</Text>
189
+
.
190
+
</Text>
191
+
}
192
+
hasInteractiveContent
193
+
>
194
+
<Icon active color="fg" name="info" paddingStart={1} role="button" tabIndex={0} />
Copy file name to clipboardExpand all lines: packages/web/CHANGELOG.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,16 @@ All notable changes to this project will be documented in this file.
8
8
9
9
<!-- template-start -->
10
10
11
+
## 8.51.0 (3/9/2026 PST)
12
+
13
+
#### 🚀 Updates
14
+
15
+
- Added hasInteractiveContent prop to Tooltip to correctly handle keyboard navigation when content includes interactive elements. [[#469](https://github.com/coinbase/cds/pull/469)][DX-5097]
16
+
17
+
#### 🐞 Fixes
18
+
19
+
- Fixed issue when tooltip does not announce its content when content is a React Node instead of a string. [[#469](https://github.com/coinbase/cds/pull/469)]
0 commit comments