diff --git a/modules/react/_examples/stories/AriaLiveRegions.stories.mdx b/modules/react/_examples/stories/AriaLiveRegions.stories.mdx
new file mode 100644
index 0000000000..446b9881f4
--- /dev/null
+++ b/modules/react/_examples/stories/AriaLiveRegions.stories.mdx
@@ -0,0 +1,76 @@
+import {AriaLiveRegion} from '@workday/canvas-kit-react/common';
+import {FilterListWithLiveStatus} from './examples/common/FilterListWithLiveStatus';
+import {VisibleLiveRegion} from './examples/common/VisibleLiveRegion';
+import {HiddenLiveRegion} from './examples/common/HiddenLiveRegion';
+import {TextInputWithLiveError} from './examples/common/TextInputWithLiveError';
+import {IconButtonsWithLiveBadges} from './examples/common/IconButtonsWithLiveBadges';
+
+
+
+# ARIA Live Regions
+
+These examples are provided to demonstrate a variety of different use cases for the `AriaLiveRegion`
+component. For the full experience, get started by first turning on your favorite screen reading
+software. On Windows, we recommend the open source
+[NVDA (Non-Visual Desktop Access)](https://www.nvaccess.org/download/) software, or
+[JAWS (Job Access With Speech)](https://support.freedomscientific.com/Downloads/JAWS) if you have
+purchased a license. MacOS and iOS include VoiceOver, which can be turned on in your settings.
+
+Live regions work by designating specific DOM nodes for screen readers to monitor for any content
+updates inside the node. When an update occurs, screen readers will announce the update to users in
+real time, based on a few rules:
+
+1. `polite` will “politely” wait for users to finish what they are doing before announcing an update
+2. `assertive` will interrupt what users are doing (or reading) by immediately announcing an update
+
+### CAUTION: Don't get carried away
+
+Key things to understand about live regions:
+
+1. A live region update will only be announced once. Users are unable to repeat them or re-examine
+ them if the announcement was not understood.
+2. Users may be able to pause a live region announcement, but they cannot prevent a live region
+ announcement from occurring. Sending frequent, repetitive, or simply too much information to a
+ live region can be very disruptive to users.
+3. Users cannot act on, or navigate to, a live region. Live regions must only contain plain text.
+ (No images, links, buttons, or other input.)
+4. Support for live regions is limited across platforms, browsers, and screen reader software. Real
+ time announcements may not be perfectly reliable.
+
+## Visible Live Regions
+
+Live regions can be applied to dynamic text on the UI. When the dynamic text is updated, screen
+readers can describe the text update in live time as it occurs. In the example below, type text into
+the input field and activate the "Send Message" button. Listen and observe the screen reader
+automatically announce the text update.
+
+
+
+## Hidden Live Regions
+
+Live regions don't need to be visible UI text, they can be used to assist the non-visual listening
+experience when moving the keyboard focus to a new element on screen isn't feasible.
+
+
+
+## Filtering lists with a live status
+
+In this example, a live region is applied to a short UI text describing the number of items shown in
+the list. As you type characters into the input, listen for the screen reader to automatically
+describe how many items in the list or shown.
+
+
+
+## Text input with live inline error
+
+In this example, a live region is applied to the inline error message that will appear below the
+text input. Listen for the screen reader to automatically describe the error message as you leave
+the input field blank.
+
+**Note:** Use this example with discretion. Using live regions for automatically announcing form
+errors to screen reader users can be a nice experience for simple forms with a very limited number
+of error conditions. As forms increase in complexity, live regions on each error message can become
+increasingly distracting and disruptive to the experience, especially if users are trying to first
+understand the information that is required of them to complete the task.
+
+
diff --git a/modules/react/_examples/stories/examples/common/FilterListWithLiveStatus.tsx b/modules/react/_examples/stories/examples/common/FilterListWithLiveStatus.tsx
new file mode 100644
index 0000000000..2568bfdf48
--- /dev/null
+++ b/modules/react/_examples/stories/examples/common/FilterListWithLiveStatus.tsx
@@ -0,0 +1,65 @@
+import React, {useState} from 'react';
+import {TextInput} from '@workday/canvas-kit-preview-react/text-input';
+import {BodyText, Heading} from '@workday/canvas-kit-react/text';
+import {AriaLiveRegion} from '@workday/canvas-kit-react/common';
+import {Flex} from '@workday/canvas-kit-react/layout';
+import {system, base} from '@workday/canvas-tokens-web';
+import {createStyles, px2rem} from '@workday/canvas-kit-styling';
+
+const fruits = [
+ 'Apples',
+ 'Oranges',
+ 'Bananas',
+ 'Lemons',
+ 'Limes',
+ 'Strawberries',
+ 'Raspberries',
+ 'Blackberries',
+];
+
+const liveRegionStyle = createStyles({
+ border: `${px2rem(1)} solid ${base.cantaloupe400}`,
+ backgroundColor: base.cantaloupe100,
+ padding: system.space.x2,
+});
+
+const listStyles = {paddingLeft: '0px'};
+
+const listItemStyles = createStyles({
+ listStyle: 'none',
+ paddingLeft: system.space.zero,
+});
+
+let filteredFruits = fruits;
+
+export const FilterListWithLiveStatus = () => {
+ const [filter, setFilter] = useState('');
+ function handleFilter(e) {
+ filteredFruits = fruits.filter(i => i.toUpperCase().indexOf(e.target.value.toUpperCase()) >= 0);
+ setFilter(e.target.value);
+ }
+
+ return (
+ <>
+
+ Fruits
+
+
+ {`Showing ${filteredFruits.length} of ${fruits.length}`}
+
+
+
+
+ Filter Items:
+
+
+
+ {filteredFruits.map(i => (
+
+ {i}
+
+ ))}
+
+ >
+ );
+};
diff --git a/modules/react/_examples/stories/examples/common/HiddenLiveRegion.tsx b/modules/react/_examples/stories/examples/common/HiddenLiveRegion.tsx
new file mode 100644
index 0000000000..355a621546
--- /dev/null
+++ b/modules/react/_examples/stories/examples/common/HiddenLiveRegion.tsx
@@ -0,0 +1,30 @@
+import React, {useState, useRef} from 'react';
+import {AriaLiveRegion, AccessibleHide} from '@workday/canvas-kit-react/common';
+import {PrimaryButton} from '@workday/canvas-kit-react/button';
+import {TextInput} from '@workday/canvas-kit-preview-react/text-input';
+import {Flex} from '@workday/canvas-kit-react/layout';
+import {system} from '@workday/canvas-tokens-web';
+
+export const HiddenLiveRegion = () => {
+ const [message, setMessage] = useState('This is an ARIA Live Region!');
+ const inputRef = useRef();
+ function handleSendMessage() {
+ setMessage(inputRef.current.value);
+ inputRef.current.value = '';
+ }
+
+ return (
+ <>
+
+
+ Type your message:
+
+
+ Send Message
+
+
+ {message}
+
+ >
+ );
+};
diff --git a/modules/react/_examples/stories/examples/common/IconButtonsWithLiveBadges.tsx b/modules/react/_examples/stories/examples/common/IconButtonsWithLiveBadges.tsx
new file mode 100644
index 0000000000..4f326d7660
--- /dev/null
+++ b/modules/react/_examples/stories/examples/common/IconButtonsWithLiveBadges.tsx
@@ -0,0 +1,98 @@
+import React, {useState} from 'react';
+import {AccessibleHide, AriaLiveRegion, useUniqueId} from '@workday/canvas-kit-react/common';
+import {notificationsIcon, inboxIcon, assistantIcon} from '@workday/canvas-system-icons-web';
+import {space} from '@workday/canvas-kit-react/tokens';
+import {SecondaryButton, TertiaryButton} from '@workday/canvas-kit-react/button';
+import {Flex} from '@workday/canvas-kit-react/layout';
+import {Tooltip} from '@workday/canvas-kit-react/tooltip';
+import {CountBadge} from '@workday/canvas-kit-react/badge';
+
+const MyTasksLiveBadge = ({cnt}) => {
+ // use tooltip to assign name,
+ // use AriaLiveRegion inside button,
+ // assign name to live region referencing the button,
+ // use BadgeCount inside live region,
+ // use AccessibleHide to create invisible word "new" after badge
+ // use aria-describedby on button, referencing live region container to set description
+ // Safari + VO => not working at all
+ // JAWS 2024 + Chrome / Edge => works as expected :)
+ // NVDA + Chrome / Edge => works as expected :)
+ // Firefox => isn't announcing description on focus, only announces "X New" live (missing button name)
+ const badgeID = useUniqueId();
+ const myTasksID = useUniqueId();
+
+ return (
+
+
+
+
+ New
+
+
+
+ );
+};
+
+// use AriaLiveRegion around the button,
+// use Tooltip to assign the name of the button,
+// make sure Tooltip title string includes count value
+// Chrome + VO => Announces name "notifications X new" and innerText 'X'
+// Safari + VO => Works as expected :)
+// JAWS 2024 => Announces full button name twice (previous state, then new state)
+// JAWS 2024 + Firefox => Works as expected :)
+// NVDA (All Browsers) => Atomic property isn't working, only announcing number change, announces twice
+const NotificationsLiveBadge = ({cnt}) => (
+
+
+
+
+
+
+
+);
+
+const AssistantLiveBadge = ({cnt}) => {
+ // use AriaLiveRegion around the button
+ // use muted type Tooltip (avoid using aria-label to name button)
+ // use AccessibleHide inside of button to compose name
+ // Chrome + VO => announces twice
+ // Safari + VO => works as expected :)
+ const lbl = 'Workday Assistant';
+
+ return (
+
+
+
+ {lbl}
+
+ New
+
+
+
+ );
+};
+
+export const IconButtonsWithLiveBadges = () => {
+ const [counter, setCounter] = useState(0);
+ const [notifications, setNotifications] = useState(0);
+ const [assistant, setAssistant] = useState(0);
+
+ const handleAddTask = () => setCounter(prev => prev + 1);
+ const handleAddNotification = () => setNotifications(prev => prev + 1);
+ const handleAssistant = () => setAssistant(prev => prev + 1);
+
+ return (
+ <>
+
+
+
+
+
+
+ Add a Message
+ Add a Notification
+ Add an item to My Tasks
+
+ >
+ );
+};
diff --git a/modules/react/_examples/stories/examples/common/TextInputWithLiveError.tsx b/modules/react/_examples/stories/examples/common/TextInputWithLiveError.tsx
new file mode 100644
index 0000000000..fb50e44385
--- /dev/null
+++ b/modules/react/_examples/stories/examples/common/TextInputWithLiveError.tsx
@@ -0,0 +1,25 @@
+import React, {useState, useRef} from 'react';
+import {TextInput} from '@workday/canvas-kit-preview-react/text-input';
+import {AriaLiveRegion, changeFocus} from '@workday/canvas-kit-react/common';
+import {PrimaryButton} from '@workday/canvas-kit-react/button';
+
+export const TextInputWithLiveError = () => {
+ const errMsg = 'Error: First name is required.';
+ const [hasError, setHasError] = useState(false);
+ const inputRef = useRef();
+ const handleBlur = e => setHasError(e.target.value.trim().length === 0);
+ const handleSubmit = () => hasError && changeFocus(inputRef.current);
+
+ return (
+ <>
+
+ First Name:
+
+
+ {hasError && errMsg}
+
+
+ Continue
+ >
+ );
+};
diff --git a/modules/react/_examples/stories/examples/common/VisibleLiveRegion.tsx b/modules/react/_examples/stories/examples/common/VisibleLiveRegion.tsx
new file mode 100644
index 0000000000..0ec7bee9f5
--- /dev/null
+++ b/modules/react/_examples/stories/examples/common/VisibleLiveRegion.tsx
@@ -0,0 +1,40 @@
+import React, {useState, useRef} from 'react';
+import {AriaLiveRegion} from '@workday/canvas-kit-react/common';
+import {PrimaryButton} from '@workday/canvas-kit-react/button';
+import {TextInput} from '@workday/canvas-kit-preview-react/text-input';
+import {Flex} from '@workday/canvas-kit-react/layout';
+import {Text} from '@workday/canvas-kit-react/text';
+import {system, base} from '@workday/canvas-tokens-web';
+import {createStyles, px2rem} from '@workday/canvas-kit-styling';
+
+const liveRegionStyle = createStyles({
+ border: `${px2rem(1)} solid ${base.cantaloupe400}`,
+ backgroundColor: base.cantaloupe100,
+ padding: system.space.x4,
+ display: 'block',
+ margin: system.space.x4 + ' 0',
+});
+
+export const VisibleLiveRegion = () => {
+ const [message, setMessage] = useState('This is an ARIA Live Region!');
+ const inputRef = useRef();
+ function handleSendMessage() {
+ setMessage(inputRef.current.value);
+ inputRef.current.value = '';
+ }
+
+ return (
+ <>
+
+ {message}
+
+
+
+ Type your message:
+
+
+ Send Message
+
+ >
+ );
+};