diff --git a/packages/svelte/src/lib/components/A11yDescriptions/A11yDescriptions.svelte b/packages/svelte/src/lib/components/A11yDescriptions/A11yDescriptions.svelte new file mode 100644 index 00000000..7904a0b9 --- /dev/null +++ b/packages/svelte/src/lib/components/A11yDescriptions/A11yDescriptions.svelte @@ -0,0 +1,30 @@ + + + + + +{#if !store.disableKeyboardA11y} +
+ {store.ariaLiveMessage} +
+{/if} diff --git a/packages/svelte/src/lib/components/A11yDescriptions/index.ts b/packages/svelte/src/lib/components/A11yDescriptions/index.ts new file mode 100644 index 00000000..af58b941 --- /dev/null +++ b/packages/svelte/src/lib/components/A11yDescriptions/index.ts @@ -0,0 +1,5 @@ +export { default as A11yDescriptions } from './A11yDescriptions.svelte'; + +export const ARIA_NODE_DESC_KEY = 'svelte-flow__node-desc'; +export const ARIA_EDGE_DESC_KEY = 'svelte-flow__edge-desc'; +export const ARIA_LIVE_MESSAGE = 'svelte-flow__aria-live'; diff --git a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte index c59729ec..c1c988fd 100644 --- a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte +++ b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte @@ -7,6 +7,7 @@ import type { Node, EdgeLayouted, Edge, EdgeEvents } from '$lib/types'; import type { SvelteFlowStore } from '$lib/store/types'; + import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions'; const { edge, @@ -104,7 +105,6 @@ }; - {#if !hidden} @@ -136,6 +136,7 @@ : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`} + aria-describedby={focusable ? `${ARIA_EDGE_DESC_KEY}-${store.flowId}` : undefined} role={focusable ? 'button' : 'img'} onkeydown={focusable ? onkeydown : undefined} tabindex={focusable ? 0 : undefined} diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index 5d54f857..2f9ebee6 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -15,6 +15,7 @@ import type { ConnectableContext, NodeWrapperProps } from './types'; import type { Node, Edge, NodeEvents } from '$lib/types'; import { toPxString } from '$lib/utils'; + import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions'; let { store = $bindable(), @@ -183,12 +184,9 @@ // prevent default scrolling behavior on arrow key press when node is moved event.preventDefault(); - // TODO: aria live message - // store.setState({ - // ariaLiveMessage: `Moved selected node ${event.key - // .replace('Arrow', '') - // .toLowerCase()}. New position, x: ${~~internals.positionAbsolute.x}, y: ${~~internals.positionAbsolute.y}`, - // }); + store.ariaLiveMessage = `Moved selected node ${event.key + .replace('Arrow', '') + .toLowerCase()}. New position, x: ${node.internals.positionAbsolute.x}, y: ${node.internals.positionAbsolute.y}`; store.moveSelectedNodes(arrowKeyDiffs[event.key], event.shiftKey ? 4 : 1); } @@ -250,6 +248,9 @@ onkeydown={focusable ? onKeyDown : undefined} tabIndex={focusable ? 0 : undefined} role={focusable ? 'button' : undefined} + aria-describedby={store.disableKeyboardA11y + ? undefined + : `${ARIA_NODE_DESC_KEY}-${store.flowId}`} > + {@render children?.()} diff --git a/packages/svelte/src/lib/store/initial-store.svelte.ts b/packages/svelte/src/lib/store/initial-store.svelte.ts index e36c1fd4..36ec8f41 100644 --- a/packages/svelte/src/lib/store/initial-store.svelte.ts +++ b/packages/svelte/src/lib/store/initial-store.svelte.ts @@ -232,6 +232,7 @@ export function getInitialStore(''); selectionMode: SelectionMode = $derived(signals.props.selectionMode ?? SelectionMode.Partial); nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes }); @@ -374,6 +375,7 @@ export function getInitialStore