Skip to content

Commit 734cc19

Browse files
authored
feat: apply inert to outroing elements (#8628)
that way they are invisible to assistive technology and can't be interacted with, which makes sense since the element is already "dead" and only transitioning out at this point closes #8445
1 parent df361a2 commit 734cc19

File tree

4 files changed

+78
-2
lines changed

4 files changed

+78
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* **breaking** Error on falsy values instead of stores passed to `derived` ([#7947](https://github.com/sveltejs/svelte/pull/7947))
1717
* **breaking** Custom store implementers now need to pass an `update` function additionally to the `set` function ([#6750](https://github.com/sveltejs/svelte/pull/6750))
1818
* **breaking** Change order in which preprocessors are applied ([#8618](https://github.com/sveltejs/svelte/pull/8618))
19+
* **breaking** apply `inert` to outroing elements ([#8627](https://github.com/sveltejs/svelte/pull/8627))
1920
* Add a way to modify attributes for script/style preprocessors ([#8618](https://github.com/sveltejs/svelte/pull/8618))
2021
* Improve hydration speed by adding `data-svelte-h` attribute to detect unchanged HTML elements ([#7426](https://github.com/sveltejs/svelte/pull/7426))
2122
* Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391))

src/runtime/internal/transitions.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,15 @@ export function create_in_transition(node, fn, params) {
186186
* @returns {{ end(reset: any): void; }}
187187
*/
188188
export function create_out_transition(node, fn, params) {
189-
/**
190-
* @type {TransitionOptions} */
189+
/** @type {TransitionOptions} */
191190
const options = { direction: 'out' };
192191
let config = fn(node, params, options);
193192
let running = true;
194193
let animation_name;
195194
const group = outros;
196195
group.r += 1;
196+
/** @type {boolean} */
197+
let original_inert_value;
197198

198199
/**
199200
* @returns {void} */
@@ -205,10 +206,18 @@ export function create_out_transition(node, fn, params) {
205206
tick = noop,
206207
css
207208
} = config || null_transition;
209+
208210
if (css) animation_name = create_rule(node, 1, 0, duration, delay, easing, css);
211+
209212
const start_time = now() + delay;
210213
const end_time = start_time + duration;
211214
add_render_callback(() => dispatch(node, false, 'start'));
215+
216+
if ('inert' in node) {
217+
original_inert_value = /** @type {HTMLElement} */ (node).inert;
218+
node.inert = true;
219+
}
220+
212221
loop((now) => {
213222
if (running) {
214223
if (now >= end_time) {
@@ -229,6 +238,7 @@ export function create_out_transition(node, fn, params) {
229238
return running;
230239
});
231240
}
241+
232242
if (is_function(config)) {
233243
wait().then(() => {
234244
// @ts-ignore
@@ -238,8 +248,12 @@ export function create_out_transition(node, fn, params) {
238248
} else {
239249
go();
240250
}
251+
241252
return {
242253
end(reset) {
254+
if (reset && 'inert' in node) {
255+
node.inert = original_inert_value;
256+
}
243257
if (reset && config.tick) {
244258
config.tick(1, 0);
245259
}
@@ -274,6 +288,9 @@ export function create_bidirectional_transition(node, fn, params, intro) {
274288
let pending_program = null;
275289
let animation_name = null;
276290

291+
/** @type {boolean} */
292+
let original_inert_value;
293+
277294
/**
278295
* @returns {void} */
279296
function clear_animation() {
@@ -318,11 +335,25 @@ export function create_bidirectional_transition(node, fn, params, intro) {
318335
start: now() + delay,
319336
b
320337
};
338+
321339
if (!b) {
322340
// @ts-ignore todo: improve typings
323341
program.group = outros;
324342
outros.r += 1;
325343
}
344+
345+
if ('inert' in node) {
346+
if (b) {
347+
if (original_inert_value !== undefined) {
348+
// aborted/reversed outro — restore previous inert value
349+
node.inert = original_inert_value;
350+
}
351+
} else {
352+
original_inert_value = /** @type {HTMLElement} */ (node).inert;
353+
node.inert = true;
354+
}
355+
}
356+
326357
if (running_program || pending_program) {
327358
pending_program = program;
328359
} else {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export default {
2+
async test({ assert, component, target, raf }) {
3+
// jsdom doesn't set the inert attribute, and the transition checks if it exists, so set it manually to trigger the inert logic
4+
target.querySelector('button.a').inert = false;
5+
target.querySelector('button.b').inert = false;
6+
7+
// check and abort halfway through the outro transition
8+
component.visible = false;
9+
raf.tick(50);
10+
assert.strictEqual(target.querySelector('button.a').inert, true);
11+
assert.strictEqual(target.querySelector('button.b').inert, true);
12+
13+
component.visible = true;
14+
assert.strictEqual(target.querySelector('button.a').inert, false);
15+
assert.strictEqual(target.querySelector('button.b').inert, false);
16+
17+
// let it transition out completely and then back in
18+
component.visible = false;
19+
raf.tick(101);
20+
component.visible = true;
21+
raf.tick(50);
22+
assert.strictEqual(target.querySelector('button.a').inert, false);
23+
assert.strictEqual(target.querySelector('button.b').inert, false);
24+
raf.tick(51);
25+
assert.strictEqual(target.querySelector('button.a').inert, false);
26+
assert.strictEqual(target.querySelector('button.b').inert, false);
27+
}
28+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script>
2+
export let visible = true;
3+
4+
function slide(_, params) {
5+
return params;
6+
}
7+
</script>
8+
9+
{#if visible}
10+
<button class="a" transition:slide={{ duration: 100 }}>
11+
foo
12+
</button>
13+
<button class="b" out:slide={{ duration: 100 }}>
14+
bar
15+
</button>
16+
{/if}

0 commit comments

Comments
 (0)