mirror of
https://github.com/shoelace-style/shoelace.git
synced 2026-01-12 11:09:13 +00:00
Improve tooltip accessibility (#1749)
* always close on escape, even when not focused; #1734 * use fallbacks instead of defaults * add words * add safe trapezoids / hover bridge; fixes #1734 * oh, webkit * remove unused import * cleanup just in case
This commit is contained in:
@@ -1530,6 +1530,140 @@ const App = () => {
|
||||
};
|
||||
```
|
||||
|
||||
### Hover Bridge
|
||||
|
||||
When a gap exists between the anchor and the popup element, this option will add a "hover bridge" that fills the gap using an invisible element. This makes listening for events such as `mouseover` and `mouseout` more sane because the pointer never technically leaves the element. The hover bridge will only be drawn when the popover is active. For demonstration purposes, the bridge in this example is shown in orange.
|
||||
|
||||
```html:preview
|
||||
<div class="popup-hover-bridge">
|
||||
<sl-popup placement="top" hover-bridge distance="10" skidding="0" active>
|
||||
<span slot="anchor"></span>
|
||||
<div class="box"></div>
|
||||
</sl-popup>
|
||||
|
||||
<br>
|
||||
<sl-switch checked>Hover Bridge</sl-switch><br>
|
||||
<sl-range min="0" max="50" step="1" value="10" label="Distance"></sl-range>
|
||||
<sl-range min="-50" max="50" step="1" value="0" label="Skidding"></sl-range>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.popup-hover-bridge span[slot='anchor'] {
|
||||
display: inline-block;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border: dashed 2px var(--sl-color-neutral-600);
|
||||
margin: 50px;
|
||||
}
|
||||
|
||||
.popup-hover-bridge .box {
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
background: var(--sl-color-primary-600);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
|
||||
.popup-hover-bridge sl-range {
|
||||
max-width: 260px;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.popup-hover-bridge sl-popup::part(hover-bridge) {
|
||||
background: tomato;
|
||||
opacity: .5;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const container = document.querySelector('.popup-hover-bridge');
|
||||
const popup = container.querySelector('sl-popup');
|
||||
const hoverBridge = container.querySelector('sl-switch');
|
||||
const distance = container.querySelector('sl-range[label="Distance"]');
|
||||
const skidding = container.querySelector('sl-range[label="Skidding"]');
|
||||
|
||||
distance.addEventListener('sl-input', () => (popup.distance = distance.value));
|
||||
skidding.addEventListener('sl-input', () => (popup.skidding = skidding.value));
|
||||
hoverBridge.addEventListener('sl-change', () => (popup.hoverBridge = hoverBridge.checked));
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import SlPopup from '@shoelace-style/shoelace/dist/react/popup';
|
||||
import SlRange from '@shoelace-style/shoelace/dist/react/range';
|
||||
import SlSwitch from '@shoelace-style/shoelace/dist/react/switch';
|
||||
|
||||
const css = `
|
||||
.popup-hover-bridge span[slot='anchor'] {
|
||||
display: inline-block;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border: dashed 2px var(--sl-color-neutral-600);
|
||||
margin: 50px;
|
||||
}
|
||||
|
||||
.popup-hover-bridge .box {
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
background: var(--sl-color-primary-600);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
}
|
||||
|
||||
.popup-hover-bridge sl-range {
|
||||
max-width: 260px;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.popup-hover-bridge sl-popup::part(hover-bridge) {
|
||||
background: tomato;
|
||||
opacity: .5;
|
||||
}
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
const [hoverBridge, setHoverBridge] = useState(true);
|
||||
const [distance, setDistance] = useState(10);
|
||||
const [skidding, setSkidding] = useState(0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="popup-hover-bridge">
|
||||
<SlPopup placement="top" hover-bridge={hoverBridge} distance={distance} skidding={skidding} active>
|
||||
<span slot="anchor" />
|
||||
<div class="box" />
|
||||
</SlPopup>
|
||||
|
||||
<br />
|
||||
<SlSwitch
|
||||
checked={hoverBridge}
|
||||
onSlChange={event => setHoverBridge(event.target.checked)}
|
||||
>
|
||||
Hover Bridge
|
||||
</SlSwitch><br />
|
||||
<SlRange
|
||||
min="0"
|
||||
max="50"
|
||||
step="1"
|
||||
value={distance}
|
||||
label="Distance"
|
||||
onSlInput={event => setDistance(event.target.value)}
|
||||
/>
|
||||
<SlRange
|
||||
min="-50"
|
||||
max="50"
|
||||
step="1"
|
||||
value={skidding}
|
||||
label="Skidding"
|
||||
onSlInput={event => setSkidding(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>{css}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Virtual Elements
|
||||
|
||||
In most cases, popups are anchored to an actual element. Sometimes, it can be useful to anchor them to a non-element. To do this, you can pass a `VirtualElement` to the anchor property. A virtual element must contain a function called `getBoundingClientRect()` that returns a [`DOMRect`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect) object as shown below.
|
||||
|
||||
@@ -12,6 +12,11 @@ Components with the <sl-badge variant="warning" pill>Experimental</sl-badge> bad
|
||||
|
||||
New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style).
|
||||
|
||||
## Next
|
||||
|
||||
- Added the `hover-bridge` feature to `<sl-popup>` to support better tooltip accessibility [#1734]
|
||||
- Improved the accessibility of `<sl-tooltip>` so they persist when hovering over the tooltip and dismiss when pressing [[Esc]] [#1734]
|
||||
|
||||
## 2.12.0
|
||||
|
||||
- Added the Italian translation [#1727]
|
||||
|
||||
Reference in New Issue
Block a user