diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css
index 53933f0ac28a2..a6d7606015802 100644
--- a/src/wp-admin/css/common.css
+++ b/src/wp-admin/css/common.css
@@ -536,12 +536,14 @@ code {
vertical-align: top;
}
-.widefat tbody th.check-column {
+.widefat tbody th.check-column,
+.widefat tbody td.check-column {
padding: 9px 0 22px;
}
.widefat thead td.check-column,
.widefat tbody th.check-column,
+.widefat tbody td.check-column,
.updates-table tbody td.check-column,
.widefat tfoot td.check-column {
padding: 11px 0 0 3px;
diff --git a/src/wp-admin/css/forms.css b/src/wp-admin/css/forms.css
index c17d038c5d2c6..dd19e1ba8070a 100644
--- a/src/wp-admin/css/forms.css
+++ b/src/wp-admin/css/forms.css
@@ -1967,7 +1967,8 @@ table.form-table td .updated p {
margin-left: 0;
}
- .wp-list-table.privacy_requests tr:not(.inline-edit-row):not(.no-items) td.column-primary:not(.check-column) {
+ .wp-list-table.privacy_requests tr:not(.inline-edit-row):not(.no-items) td.column-primary:not(.check-column),
+ .wp-list-table.privacy_requests tr:not(.inline-edit-row):not(.no-items) th.column-primary:not(.check-column) {
display: table-cell;
}
diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css
index 4eaa682127cc0..df6236e23d8d6 100644
--- a/src/wp-admin/css/list-tables.css
+++ b/src/wp-admin/css/list-tables.css
@@ -222,11 +222,13 @@
background-color: #fcf9e8;
}
-#the-comment-list .unapproved th.check-column {
+#the-comment-list .unapproved th.check-column,
+#the-comment-list .unapproved td.check-column {
border-left: 4px solid #d63638;
}
-#the-comment-list .unapproved th.check-column input {
+#the-comment-list .unapproved th.check-column input,
+#the-comment-list .unapproved td.check-column input {
margin-left: 4px;
}
@@ -1217,11 +1219,13 @@ ul.cat-checklist input[name="post_category[]"]:indeterminate::before {
------------------------------------------------------------------------------*/
.plugins tbody th.check-column,
+.plugins tbody td.check-column,
.plugins tbody {
padding: 8px 0 0 2px;
}
-.plugins tbody th.check-column input[type=checkbox] {
+.plugins tbody th.check-column input[type=checkbox],
+.plugins tbody td.check-column input[type=checkbox] {
margin-top: 4px;
}
@@ -1231,7 +1235,8 @@ ul.cat-checklist input[name="post_category[]"]:indeterminate::before {
.plugins thead td.check-column,
.plugins tfoot td.check-column,
-.plugins .inactive th.check-column {
+.plugins .inactive th.check-column,
+.plugins .inactive td.check-column {
padding-left: 6px;
}
@@ -1321,6 +1326,7 @@ ul.cat-checklist input[name="post_category[]"]:indeterminate::before {
}
.plugins .active th.check-column,
+.plugins .active td.check-column,
.plugin-update-tr.active td {
border-left: 4px solid var(--wp-admin-theme-color);
}
@@ -1406,7 +1412,8 @@ ul.cat-checklist input[name="post_category[]"]:indeterminate::before {
text-decoration: underline;
}
-.plugins tr.paused th.check-column {
+.plugins tr.paused th.check-column,
+.plugins tr.paused td.check-column {
border-left: 4px solid #b32d2e;
}
@@ -1905,7 +1912,8 @@ div.action-links,
}
.wp-list-table th.column-primary ~ th,
- .wp-list-table tr:not(.inline-edit-row):not(.no-items) td.column-primary ~ td:not(.check-column) {
+ .wp-list-table tr:not(.inline-edit-row):not(.no-items) td.column-primary ~ td:not(.check-column),
+ .wp-list-table tr:not(.inline-edit-row):not(.no-items) th.column-primary ~ td:not(.check-column) {
display: none;
}
@@ -1914,7 +1922,8 @@ div.action-links,
}
/* Checkboxes need to show */
- .wp-list-table tr th.check-column {
+ .wp-list-table tr th.check-column,
+ .wp-list-table tr td.check-column {
display: table-cell;
}
@@ -1932,11 +1941,13 @@ div.action-links,
width: auto !important; /* needs to override some columns that are more specifically targeted */
}
- .wp-list-table td.column-primary {
+ .wp-list-table td.column-primary,
+ .wp-list-table th.column-primary {
padding-right: 50px; /* space for toggle button */
}
- .wp-list-table tr:not(.inline-edit-row):not(.no-items) td.column-primary ~ td:not(.check-column) {
+ .wp-list-table tr:not(.inline-edit-row):not(.no-items) td.column-primary ~ td:not(.check-column),
+ .wp-list-table tr:not(.inline-edit-row):not(.no-items) th.column-primary ~ td:not(.check-column) {
padding: 3px 8px 3px 35%;
}
@@ -2265,12 +2276,14 @@ div.action-links,
}
.plugins tr.active + tr.inactive th.check-column,
+ .plugins tr.active + tr.inactive td.check-column,
.plugins tr.active + tr.inactive td.column-description,
.plugins .plugin-update-tr:before {
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1);
}
.plugins tr.active + tr.inactive th.check-column,
+ .plugins tr.active + tr.inactive td.check-column,
.plugins tr.active + tr.inactive td {
border-top: none;
}
@@ -2305,13 +2318,15 @@ div.action-links,
line-height: 1.5;
}
- .plugins tbody th.check-column {
+ .plugins tbody th.check-column,
+ .plugins tbody td.check-column {
padding: 8px 0 0 5px;
}
.plugins thead td.check-column,
.plugins tfoot td.check-column,
- .plugins .inactive th.check-column {
+ .plugins .inactive th.check-column,
+ .plugins .inactive td.check-column {
padding-left: 9px;
}
diff --git a/src/wp-admin/includes/class-wp-list-table.php b/src/wp-admin/includes/class-wp-list-table.php
index c243eece7035d..cfc0346bc4c2a 100644
--- a/src/wp-admin/includes/class-wp-list-table.php
+++ b/src/wp-admin/includes/class-wp-list-table.php
@@ -1767,6 +1767,26 @@ protected function column_default( $item, $column_name ) {}
*/
protected function column_cb( $item ) {}
+ /**
+ * Returns a clean, human-readable label for the primary column's row header.
+ *
+ * Used as the `aria-label` attribute value on the `
` element,
+ * giving screen readers a concise cell name instead of computing it from
+ * the full cell content (which may include row action links, excerpts, etc.).
+ *
+ * Subclasses should override this method to return the item's primary
+ * identifier (e.g. post title, plugin name, username). Return an empty string
+ * to omit the attribute.
+ *
+ * @since 6.9.0
+ *
+ * @param object|array $item The current item.
+ * @return string The aria-label value, or an empty string.
+ */
+ protected function get_primary_column_aria_label( $item ) {
+ return '';
+ }
+
/**
* Generates the columns for a single row of the table.
*
@@ -1796,9 +1816,9 @@ protected function single_row_columns( $item ) {
$attributes = "class='$classes' $data";
if ( 'cb' === $column_name ) {
- echo ' | ';
+ echo ' | ';
echo $this->column_cb( $item );
- echo '';
+ echo ' | ';
} elseif ( method_exists( $this, '_column_' . $column_name ) ) {
echo call_user_func(
array( $this, '_column_' . $column_name ),
@@ -1807,16 +1827,29 @@ protected function single_row_columns( $item ) {
$data,
$primary
);
- } elseif ( method_exists( $this, 'column_' . $column_name ) ) {
- echo "";
- echo call_user_func( array( $this, 'column_' . $column_name ), $item );
- echo $this->handle_row_actions( $item, $column_name, $primary );
- echo ' | ';
} else {
- echo "";
- echo $this->column_default( $item, $column_name );
+ $is_primary = ( $primary === $column_name );
+ $tag = $is_primary ? 'th' : 'td';
+ $scope = $is_primary ? ' scope="row"' : '';
+
+ $aria_label = '';
+ if ( $is_primary ) {
+ $label = $this->get_primary_column_aria_label( $item );
+ if ( '' !== $label ) {
+ $aria_label = ' aria-label="' . esc_attr( $label ) . '"';
+ }
+ }
+
+ echo "<$tag $attributes$scope$aria_label>";
+
+ if ( method_exists( $this, 'column_' . $column_name ) ) {
+ echo call_user_func( array( $this, 'column_' . $column_name ), $item );
+ } else {
+ echo $this->column_default( $item, $column_name );
+ }
+
echo $this->handle_row_actions( $item, $column_name, $primary );
- echo ' | ';
+ echo "$tag>";
}
}
}
diff --git a/src/wp-admin/includes/class-wp-ms-themes-list-table.php b/src/wp-admin/includes/class-wp-ms-themes-list-table.php
index a0fca2fd60fe4..81c35414d9053 100644
--- a/src/wp-admin/includes/class-wp-ms-themes-list-table.php
+++ b/src/wp-admin/includes/class-wp-ms-themes-list-table.php
@@ -940,11 +940,11 @@ public function single_row_columns( $item ) {
switch ( $column_name ) {
case 'cb':
- echo '';
+ echo ' | ';
$this->column_cb( $item );
- echo '';
+ echo ' | ';
break;
case 'name':
@@ -966,11 +966,11 @@ public function single_row_columns( $item ) {
}
}
- echo "" . $item->display( 'Name' ) . $active_theme_label . '';
+ echo " | " . $item->display( 'Name' ) . $active_theme_label . '';
$this->column_name( $item );
- echo '';
+ echo ' | ';
break;
case 'description':
diff --git a/src/wp-admin/includes/class-wp-plugins-list-table.php b/src/wp-admin/includes/class-wp-plugins-list-table.php
index 08b2e982e702f..d8945e103064e 100644
--- a/src/wp-admin/includes/class-wp-plugins-list-table.php
+++ b/src/wp-admin/includes/class-wp-plugins-list-table.php
@@ -1233,12 +1233,12 @@ public function single_row( $item ) {
switch ( $column_name ) {
case 'cb':
- echo "$checkbox | ";
+ echo "$checkbox | ";
break;
case 'name':
- echo "$plugin_name";
+ echo " | $plugin_name";
echo $this->row_actions( $actions, true );
- echo '';
+ echo ' | ';
break;
case 'description':
$classes = 'column-description desc';
diff --git a/src/wp-admin/includes/class-wp-posts-list-table.php b/src/wp-admin/includes/class-wp-posts-list-table.php
index 18c76169eb81c..f8bc624b52ffd 100644
--- a/src/wp-admin/includes/class-wp-posts-list-table.php
+++ b/src/wp-admin/includes/class-wp-posts-list-table.php
@@ -1072,10 +1072,28 @@ public function column_cb( $item ) {
* @param string $primary
*/
protected function _column_title( $post, $classes, $data, $primary ) {
- echo '';
+ $aria_label = $this->get_primary_column_aria_label( $post );
+ $aria_attr = ( '' !== $aria_label ) ? ' aria-label="' . esc_attr( $aria_label ) . '"' : '';
+ echo ' | ';
echo $this->column_title( $post );
echo $this->handle_row_actions( $post, 'title', $primary );
- echo '';
+ echo ' | ';
+ }
+
+ /**
+ * Returns a clean label for the primary (title) column's row header `aria-label`.
+ *
+ * Provides screen readers with just the post title as the row header name,
+ * preventing them from computing the name from the full cell content
+ * (which includes row action links, post states, and possibly an excerpt).
+ *
+ * @since 6.9.0
+ *
+ * @param WP_Post $item The current post object.
+ * @return string The post title, or an empty string if unavailable.
+ */
+ protected function get_primary_column_aria_label( $item ) {
+ return isset( $item->post_title ) ? $item->post_title : '';
}
/**
diff --git a/src/wp-admin/includes/class-wp-users-list-table.php b/src/wp-admin/includes/class-wp-users-list-table.php
index 9a8709b438e05..dd54b200bafaf 100644
--- a/src/wp-admin/includes/class-wp-users-list-table.php
+++ b/src/wp-admin/includes/class-wp-users-list-table.php
@@ -563,9 +563,16 @@ public function single_row( $user_object, $style = '', $role = '', $numposts = 0
$attributes = "class='$classes' $data";
if ( 'cb' === $column_name ) {
- $row .= "$checkbox | ";
+ $row .= "$checkbox | ";
} else {
- $row .= "";
+ $is_primary = ( $primary === $column_name );
+ $tag = $is_primary ? 'th' : 'td';
+ $scope = $is_primary ? ' scope="row"' : '';
+ $aria_label = '';
+ if ( $is_primary ) {
+ $aria_label = ' aria-label="' . esc_attr( $user_object->user_login ) . '"';
+ }
+ $row .= "<$tag $attributes$scope$aria_label>";
switch ( $column_name ) {
case 'username':
$row .= "$avatar $edit";
@@ -628,7 +635,8 @@ public function single_row( $user_object, $style = '', $role = '', $numposts = 0
if ( $primary === $column_name ) {
$row .= $this->row_actions( $actions );
}
- $row .= ' | ';
+ $tag = ( $primary === $column_name ) ? 'th' : 'td';
+ $row .= "$tag>";
}
}
$row .= '';