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:
2026-02-15 14:39:45 +01:00
parent 1fdb3e48c7
commit cdfd32dc69
6 changed files with 701 additions and 418 deletions

View File

@@ -68,6 +68,9 @@ class ConfigManager {
buttonContainer.id = this.options.configButtonId;
buttonContainer.className = 'config-button';
buttonContainer.title = 'Settings';
buttonContainer.setAttribute('role', 'button');
buttonContainer.setAttribute('aria-label', 'Open settings');
buttonContainer.setAttribute('tabindex', '0');
buttonContainer.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
@@ -76,6 +79,12 @@ class ConfigManager {
`;
buttonContainer.addEventListener('click', () => this.toggleConfigModal());
buttonContainer.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.toggleConfigModal();
}
});
document.body.appendChild(buttonContainer);
}
@@ -88,6 +97,9 @@ class ConfigManager {
modalContainer.id = this.options.configModalId;
modalContainer.className = 'config-modal';
modalContainer.style.display = 'none';
modalContainer.setAttribute('role', 'dialog');
modalContainer.setAttribute('aria-label', 'Settings');
modalContainer.setAttribute('aria-modal', 'true');
// Clone the template content into the modal
modalContainer.appendChild(template.content.cloneNode(true));
@@ -122,7 +134,14 @@ class ConfigManager {
this.setupTabs(modalContainer);
// Add event listeners
modalContainer.querySelector('.config-modal-close').addEventListener('click', () => this.hideConfigModal());
const closeBtn = modalContainer.querySelector('.config-modal-close');
closeBtn.addEventListener('click', () => this.hideConfigModal());
closeBtn.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.hideConfigModal();
}
});
modalContainer.querySelector('#config-cancel-button').addEventListener('click', () => this.hideConfigModal());
modalContainer.querySelector('#config-save-button').addEventListener('click', () => this.saveAndApplyConfig());
modalContainer.querySelector('#test-image-button').addEventListener('click', () => {
@@ -277,6 +296,8 @@ class ConfigManager {
hideConfigModal() {
const modal = document.getElementById(this.options.configModalId);
modal.style.display = 'none';
const configButton = document.getElementById(this.options.configButtonId);
if (configButton) configButton.focus();
}
/**
@@ -285,6 +306,8 @@ class ConfigManager {
showConfigModal() {
const modal = document.getElementById(this.options.configModalId);
modal.style.display = 'flex';
const closeBtn = modal.querySelector('.config-modal-close');
if (closeBtn) closeBtn.focus();
// Reset to first tab
const tabs = modal.querySelectorAll('.config-tab');