diff --git a/.changeset/healthy-jobs-act.md b/.changeset/healthy-jobs-act.md
new file mode 100644
index 00000000..90f89ded
--- /dev/null
+++ b/.changeset/healthy-jobs-act.md
@@ -0,0 +1,5 @@
+---
+"dom-accessibility-api": patch
+---
+
+Dynamically calculates the aria role for 
elements instead of always returning 'columnheader'
diff --git a/sources/__tests__/getRole.js b/sources/__tests__/getRole.js
index cb5b958d..3f6a5535 100644
--- a/sources/__tests__/getRole.js
+++ b/sources/__tests__/getRole.js
@@ -168,7 +168,13 @@ const cases = [
   ["title", null, createElementFactory("title", {})],
   // WARNING: Only in certain contexts
 	["td", "cell", createElementFactory("td", {})],
-	["th", "columnheader", createElementFactory("th", {})],
+	// default scope=auto
+	["th missing scope", "columnheader", createElementFactory("th", {})],
+	["th scope explicitly set to `auto`", "columnheader", createElementFactory("th", {scope:"auto"})],
+	["th scope=col", "columnheader", createElementFactory("th", {scope:"col"})],
+	["th scope=colgroup", "columnheader", createElementFactory("th", {scope:"colgroup"})],
+	["th scope=row", "rowheader", createElementFactory("th", {scope:"row"})],
+	["th scope=rowgroup", "rowheader", createElementFactory("th", {scope:"rowgroup"})],
 	["tr", "row", createElementFactory("tr", {})],
 	["track", null, createElementFactory("track", {})],
 	["ul", "list", createElementFactory("ul", {})],
@@ -182,7 +188,7 @@ const cases = [
 	["presentational  with prohibited aria attributes", null, createElementFactory("div", {'aria-label': "hello", role: "none"})],
 ];
 
-it.each(cases)("%s has the role %s", (name, role, elementFactory) => {
+it.each(cases)("%s has the role %s", (_name, role, elementFactory) => {
 	const element = elementFactory();
 
 	expect(getRole(element)).toEqual(role);
diff --git a/sources/getRole.ts b/sources/getRole.ts
index 14096a97..53f79276 100644
--- a/sources/getRole.ts
+++ b/sources/getRole.ts
@@ -59,7 +59,6 @@ const localNameToRoleMappings: Record = {
 	tfoot: "rowgroup",
 	// WARNING: Only in certain context
 	td: "cell",
-	th: "columnheader",
 	thead: "rowgroup",
 	tr: "row",
 	ul: "list",
@@ -143,12 +142,13 @@ export default function getRole(element: Element): string | null {
 }
 
 function getImplicitRole(element: Element): string | null {
-	const mappedByTag = localNameToRoleMappings[getLocalName(element)];
+	const elName = getLocalName(element);
+	const mappedByTag = localNameToRoleMappings[elName];
 	if (mappedByTag !== undefined) {
 		return mappedByTag;
 	}
 
-	switch (getLocalName(element)) {
+	switch (elName) {
 		case "a":
 		case "area":
 		case "link":
@@ -205,6 +205,17 @@ function getImplicitRole(element: Element): string | null {
 				return "listbox";
 			}
 			return "combobox";
+		case "th":
+			switch (element.getAttribute("scope")) {
+				case "row":
+				case "rowgroup":
+					return "rowheader";
+				case "col":
+				case "colgroup":
+				case "auto":
+				default:
+					return "columnheader";
+			}
 	}
 	return null;
 } |