@@ -877,9 +877,31 @@ function replaceURL() { history.replaceState(null, '', buildURL()); }
877877
878878/* ── Button handlers ─────────────────────────────────────────── */
879879
880+ // Mirror the visual 'outline' (inactive) class into aria-pressed so
881+ // the toggle buttons expose their state to assistive tech.
882+ function syncPressed ( btn ) {
883+ btn . setAttribute ( 'aria-pressed' , String ( ! btn . classList . contains ( 'outline' ) ) ) ;
884+ }
885+
886+ // Set a toolbar toggle's active state (visual class + ARIA) by id.
887+ function setToggleState ( id , active ) {
888+ var btn = document . getElementById ( id ) ;
889+ btn . classList . toggle ( 'outline' , ! active ) ;
890+ syncPressed ( btn ) ;
891+ }
892+
893+ // Keyboard activation for the sortable column headers (tabindex=0).
894+ function thSortKey ( e ) {
895+ if ( e . key === 'Enter' || e . key === ' ' ) {
896+ e . preventDefault ( ) ;
897+ e . currentTarget . click ( ) ;
898+ }
899+ }
900+
880901function toggleType ( type , btn ) {
881902 activeTypes [ type ] = ! activeTypes [ type ] ;
882903 btn . classList . toggle ( 'outline' ) ;
904+ syncPressed ( btn ) ;
883905 currentPage = 1 ;
884906 filterReports ( ) ;
885907 pushURL ( ) ;
@@ -888,6 +910,7 @@ function toggleType(type, btn) {
888910function syncToolbarButtons ( ) {
889911 document . querySelectorAll ( '.filters button[data-type]' ) . forEach ( function ( btn ) {
890912 btn . classList . toggle ( 'outline' , ! activeTypes [ btn . getAttribute ( 'data-type' ) ] ) ;
913+ syncPressed ( btn ) ;
891914 } ) ;
892915}
893916
@@ -903,6 +926,7 @@ function buildTypeFilters() {
903926 btn . setAttribute ( 'data-type' , t ) ;
904927 btn . textContent = _typeLabels [ t ] || t ;
905928 btn . addEventListener ( 'click' , function ( ) { toggleType ( t , btn ) ; } ) ;
929+ syncPressed ( btn ) ;
906930 container . appendChild ( btn ) ;
907931 } ) ;
908932}
@@ -923,6 +947,7 @@ function isolateType(type) {
923947function toggleAcked ( btn ) {
924948 onlyAcked = ! onlyAcked ;
925949 btn . classList . toggle ( 'outline' ) ;
950+ syncPressed ( btn ) ;
926951 currentPage = 1 ;
927952 filterReports ( ) ;
928953 pushURL ( ) ;
@@ -931,6 +956,7 @@ function toggleAcked(btn) {
931956function toggleOwned ( btn ) {
932957 onlyOwned = ! onlyOwned ;
933958 btn . classList . toggle ( 'outline' ) ;
959+ syncPressed ( btn ) ;
934960 currentPage = 1 ;
935961 filterReports ( ) ;
936962 pushURL ( ) ;
@@ -939,6 +965,7 @@ function toggleOwned(btn) {
939965function toggleAwaiting ( btn ) {
940966 onlyAwaiting = ! onlyAwaiting ;
941967 btn . classList . toggle ( 'outline' ) ;
968+ syncPressed ( btn ) ;
942969 currentPage = 1 ;
943970 filterReports ( ) ;
944971 pushURL ( ) ;
@@ -947,6 +974,7 @@ function toggleAwaiting(btn) {
947974function toggleOpen ( btn ) {
948975 onlyOpen = ! onlyOpen ;
949976 btn . classList . toggle ( 'outline' ) ;
977+ syncPressed ( btn ) ;
950978 currentPage = 1 ;
951979 if ( ! onlyOpen && ! closedLoaded ) {
952980 loadClosedReports ( function ( ) { filterReports ( ) ; pushURL ( ) ; } ) ;
@@ -994,8 +1022,11 @@ function sortTable(colIdx, key) {
9941022 sortState [ key ] = dir ;
9951023 document . querySelectorAll ( 'th[data-sort]' ) . forEach ( function ( th ) {
9961024 th . classList . remove ( 'asc' , 'desc' ) ;
1025+ th . removeAttribute ( 'aria-sort' ) ;
9971026 } ) ;
998- document . querySelector ( 'th[data-sort="' + key + '"]' ) . classList . add ( dir ) ;
1027+ var th = document . querySelector ( 'th[data-sort="' + key + '"]' ) ;
1028+ th . classList . add ( dir ) ;
1029+ th . setAttribute ( 'aria-sort' , dir === 'asc' ? 'ascending' : 'descending' ) ;
9991030 sortReports ( key , dir ) ;
10001031 buildDisplayList ( ) ;
10011032 currentPage = 1 ;
@@ -1034,6 +1065,7 @@ function restoreFromURL() {
10341065 }
10351066 document . querySelectorAll ( '.filters button[data-type]' ) . forEach ( function ( btn ) {
10361067 btn . classList . toggle ( 'outline' , ! activeTypes [ btn . dataset . type ] ) ;
1068+ syncPressed ( btn ) ;
10371069 } ) ;
10381070
10391071 // If the search query bypasses the Open filter via 'closed:true', any
@@ -1042,19 +1074,20 @@ function restoreFromURL() {
10421074 // Open-only filtering automatically.
10431075 var queryHasClosed = queryIncludesClosed ( qVal ) ;
10441076 onlyOpen = queryHasClosed ? true : ( params . get ( 'open' ) !== '0' ) ;
1045- document . getElementById ( 'btn-open' ) . classList . toggle ( 'outline' , ! onlyOpen ) ;
1077+ setToggleState ( 'btn-open' , onlyOpen ) ;
10461078
10471079 onlyAcked = params . get ( 'acked' ) === '1' ;
1048- document . getElementById ( 'btn-acked' ) . classList . toggle ( 'outline' , ! onlyAcked ) ;
1080+ setToggleState ( 'btn-acked' , onlyAcked ) ;
10491081
10501082 onlyOwned = params . get ( 'owned' ) === '1' ;
1051- document . getElementById ( 'btn-owned' ) . classList . toggle ( 'outline' , ! onlyOwned ) ;
1083+ setToggleState ( 'btn-owned' , onlyOwned ) ;
10521084
10531085 onlyAwaiting = params . get ( 'awaiting' ) === '1' ;
1054- document . getElementById ( 'btn-awaiting' ) . classList . toggle ( 'outline' , ! onlyAwaiting ) ;
1086+ setToggleState ( 'btn-awaiting' , onlyAwaiting ) ;
10551087
10561088 document . querySelectorAll ( 'th[data-sort]' ) . forEach ( function ( th ) {
10571089 th . classList . remove ( 'asc' , 'desc' ) ;
1090+ th . removeAttribute ( 'aria-sort' ) ;
10581091 } ) ;
10591092 sortState = { } ;
10601093 if ( params . has ( 'sort' ) && params . has ( 'dir' ) ) {
@@ -1064,6 +1097,7 @@ function restoreFromURL() {
10641097 if ( th && ( dir === 'asc' || dir === 'desc' ) ) {
10651098 sortState [ key ] = dir ;
10661099 th . classList . add ( dir ) ;
1100+ th . setAttribute ( 'aria-sort' , dir === 'asc' ? 'ascending' : 'descending' ) ;
10671101 }
10681102 } else if ( boneConfig . columnsSort ) {
10691103 // Default sort from --html-columns-sort (date -> desc, else asc).
@@ -1073,6 +1107,7 @@ function restoreFromURL() {
10731107 if ( dth ) {
10741108 sortState [ dkey ] = ddir ;
10751109 dth . classList . add ( ddir ) ;
1110+ dth . setAttribute ( 'aria-sort' , ddir === 'asc' ? 'ascending' : 'descending' ) ;
10761111 }
10771112 }
10781113
@@ -1108,6 +1143,10 @@ var _setupToggles, _showTogglesIfNeeded;
11081143 toggle . className = 'unfold' ;
11091144 toggle . textContent = '\u2026' ;
11101145 toggle . style . display = 'none' ;
1146+ // Focusable and key-activatable: the ellipsis acts as a button.
1147+ toggle . setAttribute ( 'role' , 'button' ) ;
1148+ toggle . setAttribute ( 'tabindex' , '0' ) ;
1149+ toggle . setAttribute ( 'aria-label' , 'Show full subject' ) ;
11111150 toggle . onmousedown = function ( e ) {
11121151 e . preventDefault ( ) ;
11131152 e . stopPropagation ( ) ;
@@ -1118,6 +1157,9 @@ var _setupToggles, _showTogglesIfNeeded;
11181157 td . classList . add ( 'expanded' ) ;
11191158 toggle . remove ( ) ;
11201159 } ;
1160+ toggle . onkeydown = function ( e ) {
1161+ if ( e . key === 'Enter' || e . key === ' ' ) toggle . onclick ( e ) ;
1162+ } ;
11211163 td . appendChild ( toggle ) ;
11221164 } ) ;
11231165 } ;
0 commit comments