@@ -80,6 +80,42 @@ export default function MapComponent() {
8080 return `hsla(${ hue } , ${ saturation } %, ${ lightness } %, ${ alpha } )` ;
8181 }
8282
83+ // Add this function for better contrast calculation
84+ function getAccessibleTemperatureColor ( temp , unit = 'C' , mode = 'light' ) {
85+ const tempC = unit === 'F' ? ( temp - 32 ) * 5 / 9 : parseFloat ( temp ) ;
86+
87+ const min = 0 ;
88+ const max = 30 ;
89+ const ratio = Math . min ( 1 , Math . max ( 0 , ( tempC - min ) / ( max - min ) ) ) ;
90+
91+ const hue = 270 - ratio * 270 ; // purple → red
92+ const saturation = 90 ; // Slightly reduced for better readability
93+
94+ // Ensure sufficient contrast for WCAG AA compliance
95+ const lightness = mode === 'dark' ? 45 : 35 ; // Darker colors for better contrast
96+ const alpha = 0.9 ; // Higher opacity for better visibility
97+
98+ return `hsla(${ hue } , ${ saturation } %, ${ lightness } %, ${ alpha } )` ;
99+ }
100+
101+ // Add this function to get temperature category for screen readers
102+ function getTemperatureCategory ( tempC ) {
103+ if ( tempC < 5 ) return 'Very Cold' ;
104+ if ( tempC < 15 ) return 'Cold' ;
105+ if ( tempC < 20 ) return 'Cool' ;
106+ if ( tempC < 25 ) return 'Warm' ;
107+ return 'Hot' ;
108+ }
109+
110+ // Add this function to get temperature icon/symbol
111+ function getTemperatureSymbol ( tempC ) {
112+ if ( tempC < 5 ) return '🧊' ; // ice
113+ if ( tempC < 15 ) return '❄️' ; // snowflake
114+ if ( tempC < 20 ) return '🌊' ; // wave
115+ if ( tempC < 25 ) return '🏊' ; // swimmer
116+ return '🔥' ; // fire
117+ }
118+
83119 useEffect ( ( ) => {
84120 if ( typeof window !== 'undefined' ) {
85121 window . loadedAPI = loading ;
@@ -127,6 +163,7 @@ export default function MapComponent() {
127163 const map = L . map ( mapRef . current , {
128164 center : [ 43.647216678117736 , - 79.36719310664458 ] ,
129165 zoom : 12 ,
166+ zoomControl : false ,
130167 layers : [ isDarkTheme ? darkLayer : lightLayer ]
131168 } ) ;
132169
@@ -150,6 +187,7 @@ export default function MapComponent() {
150187 // Store the cleanup function for later use
151188 map . _themeCleanup = removeThemeListener ;
152189
190+ // Update the addMarkers function
153191 function addMarkers ( items ) {
154192 items . forEach ( ( item , i ) => {
155193 const lon = item . lng || item . lon || item . Longitude ;
@@ -160,24 +198,71 @@ export default function MapComponent() {
160198 // Get current unit and format temperature
161199 const currentUnit = UnitManager . getUnit ( ) ;
162200 const formattedTemp = formatTemperature ( t , currentUnit ) ;
163- const tempColor = getTemperatureColor ( t , 'C' ) ; // Always calculate color from Celsius
201+ const tempColor = getAccessibleTemperatureColor ( t , 'C' ) ; // Use accessible color function
202+ const tempCategory = getTemperatureCategory ( t ) ;
203+ const tempSymbol = getTemperatureSymbol ( t ) ;
164204
165205 console . log ( `Plotting [${ i } ]: ${ name } @ ${ lat } ,${ lon } = ${ formattedTemp } ` ) ;
166206
167207 const icon = L . divIcon ( {
168208 className : 'custom-temp-marker' ,
169- html : `<div class="temp-label" style="background-color: ${ tempColor } ;">${ formattedTemp } </div>` ,
170- iconSize : [ 40 , 40 ] ,
171- iconAnchor : [ 20 , 20 ] ,
209+ html : `
210+ <div
211+ class="temp-label accessible-marker"
212+ style="background-color: ${ tempColor } ;"
213+ role="button"
214+ tabindex="0"
215+ aria-label="Water temperature ${ formattedTemp } at ${ name } . Category: ${ tempCategory } . Press Enter or Space to view details."
216+ data-temp-category="${ tempCategory . toLowerCase ( ) . replace ( ' ' , '-' ) } "
217+ >
218+ <span class="temp-value">${ formattedTemp } </span>
219+ </div>
220+ ` ,
221+ iconSize : [ 50 , 40 ] , // Slightly larger for better touch targets
222+ iconAnchor : [ 25 , 20 ] ,
172223 } ) ;
173224
174225 const marker = L . marker ( [ lat , lon ] , { icon } ) . addTo ( map ) ;
175226
227+ // Add function to announce to screen readers (this was missing)
228+ function announceToScreenReader ( message ) {
229+ const announcement = document . createElement ( 'div' ) ;
230+ announcement . setAttribute ( 'aria-live' , 'polite' ) ;
231+ announcement . setAttribute ( 'aria-atomic' , 'true' ) ;
232+ announcement . className = 'sr-only' ;
233+ announcement . textContent = message ;
234+
235+ document . body . appendChild ( announcement ) ;
236+
237+ // Remove after announcement
238+ setTimeout ( ( ) => {
239+ if ( document . body . contains ( announcement ) ) {
240+ document . body . removeChild ( announcement ) ;
241+ }
242+ } , 1000 ) ;
243+ }
244+
245+ // Add click handler FIRST (this is the main functionality)
176246 marker . on ( 'click' , async ( ) => {
247+ console . log ( 'Marker clicked:' , name ) ; // Debug log
248+
249+ // Announce to screen readers
250+ try {
251+ announceToScreenReader ( `Viewing details for ${ name } with water temperature ${ formattedTemp } ` ) ;
252+ } catch ( e ) {
253+ console . log ( 'Screen reader announcement failed:' , e ) ;
254+ }
255+
177256 const historicalData = await fetchHistoricalData ( name ) ;
178257
179258 if ( historicalData . length === 0 ) {
180- marker . bindPopup ( `<strong>${ name } </strong><br/>No historical data available` ) . openPopup ( ) ;
259+ marker . bindPopup ( `
260+ <div role="dialog" aria-labelledby="popup-title-${ i } ">
261+ <h3 id="popup-title-${ i } ">${ name } </h3>
262+ <p>No historical data available</p>
263+ <p>Current temperature: ${ formattedTemp } (${ tempCategory } )</p>
264+ </div>
265+ ` ) . openPopup ( ) ;
181266 return ;
182267 }
183268
@@ -375,6 +460,37 @@ export default function MapComponent() {
375460 } , 100 ) ;
376461 } ) ;
377462
463+ // Add keyboard accessibility AFTER the click handler
464+ marker . on ( 'add' , ( ) => {
465+ // Small delay to ensure DOM is ready
466+ setTimeout ( ( ) => {
467+ const markerElement = marker . getElement ( ) ;
468+ if ( markerElement ) {
469+ const tempLabel = markerElement . querySelector ( '.temp-label' ) ;
470+ if ( tempLabel ) {
471+ // Add keyboard event listeners
472+ tempLabel . addEventListener ( 'keydown' , ( e ) => {
473+ if ( e . key === 'Enter' || e . key === ' ' ) {
474+ e . preventDefault ( ) ;
475+ console . log ( 'Keyboard trigger for:' , name ) ; // Debug log
476+ marker . fire ( 'click' ) ; // This should trigger the click handler
477+ }
478+ } ) ;
479+
480+ // Add focus styling
481+ tempLabel . addEventListener ( 'focus' , ( ) => {
482+ tempLabel . style . outline = '3px solid #0066cc' ;
483+ tempLabel . style . outlineOffset = '2px' ;
484+ } ) ;
485+
486+ tempLabel . addEventListener ( 'blur' , ( ) => {
487+ tempLabel . style . outline = 'none' ;
488+ } ) ;
489+ }
490+ }
491+ } , 100 ) ;
492+ } ) ;
493+
378494 markersRef . current . push ( { marker, tempC : t , name, lat, lon } ) ;
379495 } ) ;
380496 }
0 commit comments