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:
Cory LaViska
2023-12-01 10:02:46 -05:00
committed by GitHub
parent bfa7c4cda9
commit e2b7327d98
9 changed files with 274 additions and 22 deletions

View File

@@ -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.

View File

@@ -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]