diff --git a/src/__tests__/ariaAttributes.js b/src/__tests__/ariaAttributes.js
index 7984d5e0..74cbb499 100644
--- a/src/__tests__/ariaAttributes.js
+++ b/src/__tests__/ariaAttributes.js
@@ -379,3 +379,57 @@ test('`expanded: true|false` matches `expanded` elements with proper role', () =
   expect(getByRole('button', {expanded: true})).toBeInTheDocument()
   expect(getByRole('button', {expanded: false})).toBeInTheDocument()
 })
+
+test('`disabled` throws on unsupported roles', () => {
+  const {getByRole} = render(
+    `<div role="alert" aria-disabled="true">Hello, Dave!</div>`,
+  )
+  expect(() =>
+    getByRole('alert', {disabled: true}),
+  ).toThrowErrorMatchingInlineSnapshot(
+    `"aria-disabled" is not supported on role "alert".`,
+  )
+})
+
+test('`disabled: true|false` matches `disabled` buttons', () => {
+  const {getByRole} = renderIntoDocument(
+    `<div>
+      <button disabled="true" />
+      <button />
+    </div>`,
+  )
+  expect(getByRole('button', {disabled: true})).toBeInTheDocument()
+  expect(getByRole('button', {disabled: false})).toBeInTheDocument()
+})
+
+test('`disabled: true|false` matches `aria-disabled` buttons', () => {
+  const {getByRole} = renderIntoDocument(
+    `<div>
+      <button aria-disabled="true" />
+      <button aria-disabled="false" />
+    </div>`,
+  )
+  expect(getByRole('button', {disabled: true})).toBeInTheDocument()
+  expect(getByRole('button', {disabled: false})).toBeInTheDocument()
+})
+
+test('`disabled` attributes overrides `aria-disabled`', () => {
+  const {getByRole} = renderIntoDocument(
+    `<div>
+      <button disabled="true" aria-disabled="false" />
+      <button />
+    </div>`,
+  )
+  expect(getByRole('button', {disabled: true})).toBeInTheDocument()
+})
+
+test('consider `disabled` attribute only if supported', () => {
+  const {getByRole, queryByRole} = renderIntoDocument(
+    `<div>
+      <button disabled="true" />
+      <div role="slider" disabled="true" />
+    </div>`,
+  )
+  expect(getByRole('button', {disabled: true})).toBeInTheDocument()
+  expect(queryByRole('slider', {disabled: true})).toBe(null)
+})
diff --git a/src/queries/role.ts b/src/queries/role.ts
index 98b08848..902e5431 100644
--- a/src/queries/role.ts
+++ b/src/queries/role.ts
@@ -14,6 +14,7 @@ import {
   computeAriaChecked,
   computeAriaPressed,
   computeAriaCurrent,
+  computeAriaDisabled,
   computeAriaExpanded,
   computeAriaValueNow,
   computeAriaValueMax,
@@ -52,6 +53,7 @@ const queryAllByRole: AllByRole = (
     checked,
     pressed,
     current,
+    disabled,
     level,
     expanded,
     value: {
@@ -174,6 +176,16 @@ const queryAllByRole: AllByRole = (
     }
   }
 
+  if (disabled !== undefined) {
+    // guard against unknown roles
+    if (
+      allRoles.get(role as ARIARoleDefinitionKey)?.props['aria-disabled'] ===
+      undefined
+    ) {
+      throw new Error(`"aria-disabled" is not supported on role "${role}".`)
+    }
+  }
+
   const subtreeIsInaccessibleCache = new WeakMap<Element, Boolean>()
   function cachedIsSubtreeInaccessible(element: Element) {
     if (!subtreeIsInaccessibleCache.has(element)) {
@@ -227,6 +239,9 @@ const queryAllByRole: AllByRole = (
       if (current !== undefined) {
         return current === computeAriaCurrent(element)
       }
+      if (disabled !== undefined) {
+        return disabled === computeAriaDisabled(element)
+      }
       if (expanded !== undefined) {
         return expanded === computeAriaExpanded(element)
       }
diff --git a/src/role-helpers.js b/src/role-helpers.js
index bc134f27..3a067606 100644
--- a/src/role-helpers.js
+++ b/src/role-helpers.js
@@ -290,6 +290,29 @@ function computeAriaCurrent(element) {
   )
 }
 
+const elementsSupportingDisabledAttribute = new Set([
+  'button',
+  'fieldset',
+  'input',
+  'optgroup',
+  'option',
+  'select',
+  'textarea',
+])
+
+/**
+ * @param {Element} element -
+ * @returns {boolean} -
+ */
+function computeAriaDisabled(element) {
+  return elementsSupportingDisabledAttribute.has(element.localName) &&
+    element.hasAttribute('disabled')
+    ? // https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
+      true
+    : // https://www.w3.org/TR/wai-aria-1.1/#aria-disabled
+      element.getAttribute('aria-disabled') === 'true'
+}
+
 /**
  * @param {Element} element -
  * @returns {boolean | undefined} - false/true if (not)expanded, undefined if not expand-able
@@ -382,6 +405,7 @@ export {
   computeAriaChecked,
   computeAriaPressed,
   computeAriaCurrent,
+  computeAriaDisabled,
   computeAriaExpanded,
   computeAriaValueNow,
   computeAriaValueMax,
diff --git a/types/queries.d.ts b/types/queries.d.ts
index c6ce9054..3848f99d 100644
--- a/types/queries.d.ts
+++ b/types/queries.d.ts
@@ -99,6 +99,11 @@ export interface ByRoleOptions {
    * Filters elements by their `aria-current` state. `true` and `false` match `aria-current="true"` and `aria-current="false"` (as well as a missing `aria-current` attribute) respectively.
    */
   current?: boolean | string
+  /**
+   * If true only includes elements in the query set that are marked as
+   * disabled in the accessibility tree, i.e., `aria-disabled="true"` or `disabled="true"`.
+   */
+  disabled?: boolean
   /**
    * If true only includes elements in the query set that are marked as
    * expanded in the accessibility tree, i.e., `aria-expanded="true"`