@@ -63,17 +63,21 @@ const init = (): void => {
6363 // View toggle tabs
6464 const viewTabs = document . createElement ( "div" ) ;
6565 viewTabs . id = "fallow-view-tabs" ;
66+ viewTabs . setAttribute ( "role" , "group" ) ;
67+ viewTabs . setAttribute ( "aria-label" , "Visualization view" ) ;
6668
6769 const treemapTab = document . createElement ( "button" ) ;
6870 treemapTab . className = "view-tab active" ;
6971 treemapTab . textContent = "Treemap" ;
7072 treemapTab . dataset . view = "treemap" ;
73+ treemapTab . setAttribute ( "aria-pressed" , "true" ) ;
7174 viewTabs . appendChild ( treemapTab ) ;
7275
7376 const graphTab = document . createElement ( "button" ) ;
7477 graphTab . className = "view-tab" ;
7578 graphTab . textContent = "Graph" ;
7679 graphTab . dataset . view = "graph" ;
80+ graphTab . setAttribute ( "aria-pressed" , "false" ) ;
7781 viewTabs . appendChild ( graphTab ) ;
7882
7983 controls . appendChild ( viewTabs ) ;
@@ -82,6 +86,8 @@ const init = (): void => {
8286 const filterGroup = document . createElement ( "div" ) ;
8387 filterGroup . id = "fallow-graph-filters" ;
8488 filterGroup . style . display = "none" ;
89+ filterGroup . setAttribute ( "role" , "group" ) ;
90+ filterGroup . setAttribute ( "aria-label" , "Filter graph nodes" ) ;
8591
8692 const filterButtons : Array < { label : string ; value : GraphFilter } > = [
8793 { label : "All" , value : "all" } ,
@@ -91,12 +97,16 @@ const init = (): void => {
9197
9298 for ( const fb of filterButtons ) {
9399 const btn = document . createElement ( "button" ) ;
94- btn . className = `filter-btn${ fb . value === "all" ? " active" : "" } ` ;
100+ const isActive = fb . value === "all" ;
101+ btn . className = `filter-btn${ isActive ? " active" : "" } ` ;
95102 btn . textContent = fb . label ;
96103 btn . dataset . filter = fb . value ;
104+ btn . setAttribute ( "aria-pressed" , String ( isActive ) ) ;
97105 btn . addEventListener ( "click" , ( ) => {
98106 for ( const b of filterGroup . querySelectorAll ( ".filter-btn" ) ) {
99- b . classList . toggle ( "active" , ( b as HTMLElement ) . dataset . filter === fb . value ) ;
107+ const on = ( b as HTMLElement ) . dataset . filter === fb . value ;
108+ b . classList . toggle ( "active" , on ) ;
109+ b . setAttribute ( "aria-pressed" , String ( on ) ) ;
100110 }
101111 setGraphFilter ( state ! , fb . value ) ;
102112 } ) ;
@@ -108,6 +118,8 @@ const init = (): void => {
108118 const clusterGroup = document . createElement ( "div" ) ;
109119 clusterGroup . id = "fallow-cluster-mode" ;
110120 clusterGroup . style . display = "none" ;
121+ clusterGroup . setAttribute ( "role" , "group" ) ;
122+ clusterGroup . setAttribute ( "aria-label" , "Graph clustering mode" ) ;
111123
112124 const clusterLabel = document . createElement ( "span" ) ;
113125 clusterLabel . className = "cluster-label" ;
@@ -121,12 +133,16 @@ const init = (): void => {
121133
122134 for ( const cb of clusterButtons ) {
123135 const btn = document . createElement ( "button" ) ;
124- btn . className = `filter-btn${ cb . value === "directory" ? " active" : "" } ` ;
136+ const isActive = cb . value === "directory" ;
137+ btn . className = `filter-btn${ isActive ? " active" : "" } ` ;
125138 btn . textContent = cb . label ;
126139 btn . dataset . cluster = cb . value ;
140+ btn . setAttribute ( "aria-pressed" , String ( isActive ) ) ;
127141 btn . addEventListener ( "click" , ( ) => {
128142 for ( const b of clusterGroup . querySelectorAll ( ".filter-btn" ) ) {
129- b . classList . toggle ( "active" , ( b as HTMLElement ) . dataset . cluster === cb . value ) ;
143+ const on = ( b as HTMLElement ) . dataset . cluster === cb . value ;
144+ b . classList . toggle ( "active" , on ) ;
145+ b . setAttribute ( "aria-pressed" , String ( on ) ) ;
130146 }
131147 setClusterMode ( state ! , cb . value ) ;
132148 } ) ;
@@ -140,6 +156,14 @@ const init = (): void => {
140156
141157 const canvas = document . createElement ( "canvas" ) ;
142158 canvas . id = "fallow-canvas" ;
159+ canvas . tabIndex = 0 ;
160+ canvas . setAttribute ( "role" , "img" ) ;
161+ canvas . setAttribute (
162+ "aria-label" ,
163+ `Interactive visualization of ${ data . summary . total_files } files. ` +
164+ "Use the Treemap and Graph buttons to switch views, the search box to find files, " +
165+ "and the number keys 1-3 to set graph focus depth." ,
166+ ) ;
143167 app . appendChild ( canvas ) ;
144168
145169 const state = createState ( data , canvas ) ;
@@ -161,11 +185,15 @@ const init = (): void => {
161185 darkModeBtn . id = "fallow-darkmode" ;
162186 darkModeBtn . textContent = state . darkMode ? "☀" : "☾" ;
163187 darkModeBtn . title = "Toggle dark mode" ;
188+ darkModeBtn . setAttribute ( "aria-label" , "Toggle dark mode" ) ;
189+ darkModeBtn . setAttribute ( "aria-pressed" , String ( state . darkMode ) ) ;
164190 controls . appendChild ( darkModeBtn ) ;
165191
166192 // Legend
167193 const legend = document . createElement ( "div" ) ;
168194 legend . id = "fallow-legend" ;
195+ legend . setAttribute ( "role" , "list" ) ;
196+ legend . setAttribute ( "aria-label" , "Status legend" ) ;
169197 const legendItems : Array < [ string , string ] > = [
170198 [ "Clean" , state . theme . statusColors . clean ] ,
171199 [ "Entry point" , state . theme . statusColors . entryPoint ] ,
@@ -175,10 +203,12 @@ const init = (): void => {
175203 for ( const [ label , color ] of legendItems ) {
176204 const item = document . createElement ( "span" ) ;
177205 item . className = "legend-item" ;
206+ item . setAttribute ( "role" , "listitem" ) ;
178207
179208 const swatch = document . createElement ( "span" ) ;
180209 swatch . className = "legend-swatch" ;
181210 swatch . style . backgroundColor = color ;
211+ swatch . setAttribute ( "aria-hidden" , "true" ) ;
182212 item . appendChild ( swatch ) ;
183213
184214 const text = document . createElement ( "span" ) ;
@@ -196,6 +226,8 @@ const init = (): void => {
196226 state . activeView = view ;
197227 treemapTab . classList . toggle ( "active" , view === "treemap" ) ;
198228 graphTab . classList . toggle ( "active" , view === "graph" ) ;
229+ treemapTab . setAttribute ( "aria-pressed" , String ( view === "treemap" ) ) ;
230+ graphTab . setAttribute ( "aria-pressed" , String ( view === "graph" ) ) ;
199231 breadcrumbEl . style . display = view === "treemap" ? "" : "none" ;
200232 filterGroup . style . display = view === "graph" ? "" : "none" ;
201233 clusterGroup . style . display = view === "graph" ? "" : "none" ;
0 commit comments