Items 16-22: Icon classification, accessibility, responsive design, API params
- Consolidate 5 weather icon methods into classifyWeatherIcon/applyWeatherIconClasses - Add focus-visible styles, ARIA attributes, keyboard nav on config button/modal - Add responsive breakpoints for departure cards, weather widget, config modal - Simplify CSS selectors: replace :not(:is(...)) chains with body.normal - Fix upsidedown layout margin assumption with transform-based centering - Upgrade weather icons from @2x to @4x for high-DPI displays - Add forecast window and transport filter params to SL departures API Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -195,43 +195,42 @@ class WeatherManager {
|
||||
* Get weather icon URL from icon code
|
||||
*/
|
||||
getWeatherIconUrl(iconCode) {
|
||||
return `https://openweathermap.org/img/wn/${iconCode}@2x.png`;
|
||||
return `https://openweathermap.org/img/wn/${iconCode}@4x.png`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if icon represents sun (even behind clouds)
|
||||
* Classify a weather icon and return the appropriate CSS classes
|
||||
* @param {string} iconCode - OWM icon code (e.g. '01d', '13n')
|
||||
* @param {string} condition - Weather condition text (e.g. 'Clear', 'Clouds')
|
||||
* @returns {string[]} Array of CSS class names to apply
|
||||
*/
|
||||
isSunIcon(iconCode, condition) {
|
||||
// Icon codes: 01d, 01n = clear, 02d, 02n = few clouds, 03d, 03n = scattered, 04d, 04n = broken clouds
|
||||
const sunIconCodes = ['01d', '01n', '02d', '02n', '03d', '03n', '04d', '04n'];
|
||||
return sunIconCodes.includes(iconCode) ||
|
||||
condition.includes('Clear') ||
|
||||
condition.includes('Clouds');
|
||||
classifyWeatherIcon(iconCode, condition) {
|
||||
const code = iconCode ? iconCode.replace(/[dn]$/, '') : '';
|
||||
|
||||
// Snow: icon 13x or condition contains 'Snow'
|
||||
if (code === '13' || condition.includes('Snow')) {
|
||||
return ['weather-snow'];
|
||||
}
|
||||
// Clear sun: icon 01x or condition is exactly 'Clear'
|
||||
if (code === '01' || condition === 'Clear') {
|
||||
return ['weather-sun', 'weather-clear-sun'];
|
||||
}
|
||||
// Sun behind clouds: icon 02-04x or cloudy condition
|
||||
if (['02', '03', '04'].includes(code) || (condition.includes('Clouds') && !condition.includes('Clear'))) {
|
||||
return ['weather-sun', 'weather-clouds-sun'];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if icon is clear sun (no clouds)
|
||||
* Apply weather icon CSS classes to an element
|
||||
*/
|
||||
isClearSun(iconCode, condition) {
|
||||
const clearIconCodes = ['01d', '01n'];
|
||||
return clearIconCodes.includes(iconCode) || condition === 'Clear';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if icon is sun behind clouds
|
||||
*/
|
||||
isSunBehindClouds(iconCode, condition) {
|
||||
const cloudIconCodes = ['02d', '02n', '03d', '03n', '04d', '04n'];
|
||||
return cloudIconCodes.includes(iconCode) || (condition.includes('Clouds') && !condition.includes('Clear'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if icon represents snow
|
||||
*/
|
||||
isSnowIcon(iconCode, condition) {
|
||||
// Icon code: 13d, 13n = snow
|
||||
const snowIconCodes = ['13d', '13n'];
|
||||
return snowIconCodes.includes(iconCode) || condition.includes('Snow');
|
||||
applyWeatherIconClasses(element, iconCode, condition) {
|
||||
element.classList.remove('weather-sun', 'weather-snow', 'weather-clear-sun', 'weather-clouds-sun');
|
||||
const classes = this.classifyWeatherIcon(iconCode, condition);
|
||||
if (classes.length > 0) {
|
||||
element.classList.add(...classes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +241,7 @@ class WeatherManager {
|
||||
temperature: 7.1,
|
||||
condition: 'Clear',
|
||||
description: 'clear sky',
|
||||
icon: 'https://openweathermap.org/img/wn/01d@2x.png',
|
||||
icon: 'https://openweathermap.org/img/wn/01d@4x.png',
|
||||
iconCode: '01d',
|
||||
wind: {
|
||||
speed: 14.8,
|
||||
@@ -273,7 +272,7 @@ class WeatherManager {
|
||||
temperature: 7.1 - (i * 0.3), // Decrease temperature slightly each hour
|
||||
condition: i < 2 ? 'Clear' : 'Clouds',
|
||||
description: i < 2 ? 'clear sky' : 'few clouds',
|
||||
icon: i < 2 ? 'https://openweathermap.org/img/wn/01n@2x.png' : 'https://openweathermap.org/img/wn/02n@2x.png',
|
||||
icon: i < 2 ? 'https://openweathermap.org/img/wn/01n@4x.png' : 'https://openweathermap.org/img/wn/02n@4x.png',
|
||||
iconCode: i < 2 ? '01n' : '02n',
|
||||
timestamp: forecastTime,
|
||||
precipitation: 0
|
||||
@@ -305,18 +304,8 @@ class WeatherManager {
|
||||
if (iconElement) {
|
||||
iconElement.src = this.weatherData.icon;
|
||||
iconElement.alt = this.weatherData.description;
|
||||
// Add classes and data attributes for color filtering
|
||||
iconElement.setAttribute('data-condition', this.weatherData.condition);
|
||||
iconElement.classList.remove('weather-sun', 'weather-snow', 'weather-clear-sun', 'weather-clouds-sun');
|
||||
if (this.isSnowIcon(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
iconElement.classList.add('weather-snow');
|
||||
} else if (this.isClearSun(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
iconElement.classList.add('weather-sun', 'weather-clear-sun');
|
||||
} else if (this.isSunBehindClouds(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
iconElement.classList.add('weather-sun', 'weather-clouds-sun');
|
||||
} else if (this.isSunIcon(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
iconElement.classList.add('weather-sun');
|
||||
}
|
||||
this.applyWeatherIconClasses(iconElement, this.weatherData.iconCode, this.weatherData.condition);
|
||||
}
|
||||
|
||||
const temperatureElement = document.querySelector('#custom-weather .temperature');
|
||||
@@ -338,15 +327,7 @@ class WeatherManager {
|
||||
nowIcon.alt = this.weatherData.description;
|
||||
nowIcon.width = 56;
|
||||
nowIcon.setAttribute('data-condition', this.weatherData.condition);
|
||||
if (this.isSnowIcon(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
nowIcon.classList.add('weather-snow');
|
||||
} else if (this.isClearSun(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
nowIcon.classList.add('weather-sun', 'weather-clear-sun');
|
||||
} else if (this.isSunBehindClouds(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
nowIcon.classList.add('weather-sun', 'weather-clouds-sun');
|
||||
} else if (this.isSunIcon(this.weatherData.iconCode, this.weatherData.condition)) {
|
||||
nowIcon.classList.add('weather-sun');
|
||||
}
|
||||
this.applyWeatherIconClasses(nowIcon, this.weatherData.iconCode, this.weatherData.condition);
|
||||
nowElement.innerHTML = `
|
||||
<div class="time">Nu</div>
|
||||
<div class="icon"></div>
|
||||
@@ -367,15 +348,7 @@ class WeatherManager {
|
||||
forecastIcon.alt = forecast.description;
|
||||
forecastIcon.width = 56;
|
||||
forecastIcon.setAttribute('data-condition', forecast.condition);
|
||||
if (this.isSnowIcon(forecast.iconCode, forecast.condition)) {
|
||||
forecastIcon.classList.add('weather-snow');
|
||||
} else if (this.isClearSun(forecast.iconCode, forecast.condition)) {
|
||||
forecastIcon.classList.add('weather-sun', 'weather-clear-sun');
|
||||
} else if (this.isSunBehindClouds(forecast.iconCode, forecast.condition)) {
|
||||
forecastIcon.classList.add('weather-sun', 'weather-clouds-sun');
|
||||
} else if (this.isSunIcon(forecast.iconCode, forecast.condition)) {
|
||||
forecastIcon.classList.add('weather-sun');
|
||||
}
|
||||
this.applyWeatherIconClasses(forecastIcon, forecast.iconCode, forecast.condition);
|
||||
forecastElement.innerHTML = `
|
||||
<div class="time">${timeString}</div>
|
||||
<div class="icon"></div>
|
||||
|
||||
Reference in New Issue
Block a user