diff --git a/.changeset/khaki-rules-breathe.md b/.changeset/khaki-rules-breathe.md
new file mode 100644
index 00000000000..99489c91f19
--- /dev/null
+++ b/.changeset/khaki-rules-breathe.md
@@ -0,0 +1,7 @@
+---
+"@primer/react": patch
+---
+
+Use `aria-required` instead of `required` on required form elements
+
+
diff --git a/src/TextInput/TextInput.features.stories.tsx b/src/TextInput/TextInput.features.stories.tsx
index 51c9c7ef584..765c6210551 100644
--- a/src/TextInput/TextInput.features.stories.tsx
+++ b/src/TextInput/TextInput.features.stories.tsx
@@ -83,6 +83,15 @@ export const Large = () => (
)
+export const Required = () => (
+
+
+ Default label
+
+
+
+)
+
export const WithLeadingVisual = () => (
diff --git a/src/TextInput/TextInput.tsx b/src/TextInput/TextInput.tsx
index 9c6178004e3..5250f510b6e 100644
--- a/src/TextInput/TextInput.tsx
+++ b/src/TextInput/TextInput.tsx
@@ -78,6 +78,7 @@ const TextInput = React.forwardRef(
variant: variantProp,
// end deprecated props
type = 'text',
+ required,
...inputProps
},
ref,
@@ -143,6 +144,7 @@ const TextInput = React.forwardRef(
onFocus={handleInputFocus}
onBlur={handleInputBlur}
type={type}
+ aria-required={required ? 'true' : 'false'}
{...inputProps}
data-component="input"
/>
diff --git a/src/Textarea/Textarea.tsx b/src/Textarea/Textarea.tsx
index f07e1aab840..669e595c7d5 100644
--- a/src/Textarea/Textarea.tsx
+++ b/src/Textarea/Textarea.tsx
@@ -92,7 +92,6 @@ const Textarea = React.forwardRef(
{
const input = getByRole('textbox')
- expect(input.getAttribute('required')).not.toBeNull()
+ expect(input).toHaveAttribute('aria-required', 'true')
})
it('renders with a caption', () => {
diff --git a/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap b/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap
index a1944410067..080ff22b479 100644
--- a/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap
+++ b/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap
@@ -118,6 +118,7 @@ exports[`snapshots renders a custom empty state message 1`] = `
aria-expanded={false}
aria-haspopup="listbox"
aria-owns="autocompleteId-listbox"
+ aria-required="false"
autoComplete="off"
className="c2"
data-component="input"
@@ -285,6 +286,7 @@ exports[`snapshots renders a loading state 1`] = `
aria-expanded={false}
aria-haspopup="listbox"
aria-owns="autocompleteId-listbox"
+ aria-required="false"
autoComplete="off"
className="c2"
data-component="input"
@@ -503,6 +505,7 @@ exports[`snapshots renders a menu that contains an item to add to the menu 1`] =
aria-expanded={false}
aria-haspopup="listbox"
aria-owns="autocompleteId-listbox"
+ aria-required="false"
autoComplete="off"
className="c2"
data-component="input"
@@ -1273,6 +1276,7 @@ exports[`snapshots renders a multiselect input 1`] = `
aria-expanded={false}
aria-haspopup="listbox"
aria-owns="autocompleteId-listbox"
+ aria-required="false"
autoComplete="off"
className="c2"
data-component="input"
@@ -1946,6 +1950,7 @@ exports[`snapshots renders a multiselect input with selected menu items 1`] = `
aria-expanded={false}
aria-haspopup="listbox"
aria-owns="autocompleteId-listbox"
+ aria-required="false"
autoComplete="off"
className="c2"
data-component="input"
@@ -2751,6 +2756,7 @@ exports[`snapshots renders a single select input 1`] = `
aria-expanded={false}
aria-haspopup="listbox"
aria-owns="autocompleteId-listbox"
+ aria-required="false"
autoComplete="off"
className="c2"
data-component="input"
@@ -3775,6 +3781,7 @@ exports[`snapshots renders with an input value 1`] = `
aria-expanded={false}
aria-haspopup="listbox"
aria-owns="autocompleteId-listbox"
+ aria-required="false"
autoComplete="off"
className="c2"
data-component="input"
diff --git a/src/__tests__/__snapshots__/TextInput.test.tsx.snap b/src/__tests__/__snapshots__/TextInput.test.tsx.snap
index e891006f9dd..63eaeab5e13 100644
--- a/src/__tests__/__snapshots__/TextInput.test.tsx.snap
+++ b/src/__tests__/__snapshots__/TextInput.test.tsx.snap
@@ -112,6 +112,7 @@ exports[`TextInput renders 1`] = `
onClick={[Function]}
>
{
it('does not require the textarea by default', async () => {
const {getInput} = await render()
- expect(getInput()).not.toHaveAttribute('required')
+ expect(getInput()).toHaveAttribute('aria-required', 'false')
})
it('requires the textarea when required', async () => {
const {getInput} = await render()
- expect(getInput()).toHaveAttribute('required')
+ expect(getInput()).toHaveAttribute('aria-required', 'true')
})
it('does not render a placeholder by default', async () => {