Compare commits

..

16 commits
v0.3.1 ... main

Author SHA1 Message Date
16175c2d17 v0.6.4: update filter styles 2024-06-10 14:47:44 +02:00
2a0b6dbfe6 v0.6.3: immediate update on new version (skipping 24h service worker cooldown) 2024-06-10 13:55:39 +02:00
12048f3a49 v0.6.2: update favicon 2024-06-09 18:54:55 +02:00
085329929f v0.6.1: minor fixes
- fixed modal style
- modified some texts
- worked on stationsNearMe functionality (WIP)
- correct sorting of OSM level list
2024-06-09 18:42:15 +02:00
e72ad0cf07 add offline functionality by making this a PWA
- added favicons
- added manifest
- split javascript into main.js and elevators.js
2024-06-08 23:12:00 +02:00
192e59d9f8 refactor color usage 2024-06-07 16:39:15 +02:00
fda2214aea warning when data is older than 1 day 2024-06-07 13:00:19 +02:00
136eabc29b new version handling; alternative search strings 2024-06-07 12:59:09 +02:00
9cfcfaee67 display filter info 2024-06-06 15:31:42 +02:00
6ebc758e25 minor layout fix 2024-06-06 15:22:15 +02:00
9a6be1d010 display additional information for station 2024-06-06 15:16:18 +02:00
caa96f565f improve browser compatability
in some browsers ariaPressed is only supported since 2023. We use a data attribute as a fallback
2024-06-06 14:38:23 +02:00
14a514e3e4 some major improvements
- filter by station type
- sort by distance (WIP)
- detect old data structure and clear localStorage if not compatible
- add substitue coordinates for stations without valid osm node
- improve responsive styles
2024-06-06 11:27:16 +02:00
af50e0f04d minor template and layout fixes 2024-06-05 11:49:38 +02:00
d9a21b53be only german for now. add footer. reposition last updated info. 2024-06-05 11:20:59 +02:00
0639aeae6f reposition some buttons 2024-06-05 11:08:32 +02:00
25 changed files with 2287 additions and 289 deletions

View file

@ -6,35 +6,43 @@
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="style.css" rel="stylesheet"> <link href="style.css" rel="stylesheet">
<title>A11y Elevator List</title> <link rel="icon" type="image/png" sizes="512" href="/icons/512.png">
<link rel="icon" type="image/png" sizes="192" href="/icons/192.png">
<link rel="shortcut icon" href="/icons/favicon.svg">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/icons/favicon-transparent.svg" color="#65a30d">
<title>hvvstuhl.de | Was ist das?</title>
</head> </head>
<body> <body>
<h1>A11y Elevator List</h1> <h1>Barrierefreie Aufzugs-Liste</h1>
<nav> <nav>
<ul> <ul>
<li> <li>
<a href="index.html"> <a href="index.html">
Elevator List Aufzugs-Liste
</a> </a>
</li> </li>
<li> <li>
<a href="about.html"> <a href="about.html">
What is this? Was ist das?
</a> </a>
</li> </li>
</ul> </ul>
</nav> </nav>
<main> <main>
<section> <section>
<h2>What is this site?</h2> <h2>Worum geht's hier?</h2>
<p> <p>
This site provides an (at least more) accessible version of the HVV Website for (not) working elevators:<br> Diese Seite ermöglicht einen barrierefreien Zugang zu den Aufzugs-Informationen im HVV.
<a href="https://www.hvv.de/de/aufzuege">HVV Website</a> Der HVV selbst zeigt diese Informationen nur auf einer (vor allem ohne Ortskenntnis) schwer zu navigierenden
Karte an:
<a href="https://www.hvv.de/de/aufzuege">HVV Webseite zu Aufzügen</a>
</p> </p>
<p> <p>
The Source code of this page is available under: <a href="https://git.kritzl.dev/kritzl/hvvstuhl.de" target="_blank">git.kritzl.dev</a> Dieses Projekt wurde unter der AGPL-3 Lizenz entwickelt und ist hier zu finden:
<a href="https://git.kritzl.dev/kritzl/hvvstuhl.de" target="_blank">git.kritzl.dev</a>
</p> </p>
</section> </section>
</main> </main>
</body> </body>
</html> </html>

3
colors.css Normal file
View file

@ -0,0 +1,3 @@
/*# sourceMappingURL=colors.css.map */

1
colors.css.map Normal file
View file

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"","file":"colors.css"}

271
colors.scss Normal file
View file

@ -0,0 +1,271 @@
@mixin tailwindColors {
/* Colors from Tailwind CSS: https://github.com/tailwindlabs/tailwindcss
MIT License
Copyright (c) Tailwind Labs, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
--color-black: #000;
--color-white: #fff;
--color-slate-50: #f8fafc;
--color-slate-100: #f1f5f9;
--color-slate-200: #e2e8f0;
--color-slate-300: #cbd5e1;
--color-slate-400: #94a3b8;
--color-slate-500: #64748b;
--color-slate-600: #475569;
--color-slate-700: #334155;
--color-slate-800: #1e293b;
--color-slate-900: #0f172a;
--color-slate-950: #020617;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
--color-gray-950: #030712;
--color-zinc-50: #fafafa;
--color-zinc-100: #f4f4f5;
--color-zinc-200: #e4e4e7;
--color-zinc-300: #d4d4d8;
--color-zinc-400: #a1a1aa;
--color-zinc-500: #71717a;
--color-zinc-600: #52525b;
--color-zinc-700: #3f3f46;
--color-zinc-800: #27272a;
--color-zinc-900: #18181b;
--color-zinc-950: #09090b;
--color-neutral-50: #fafafa;
--color-neutral-100: #f5f5f5;
--color-neutral-200: #e5e5e5;
--color-neutral-300: #d4d4d4;
--color-neutral-400: #a3a3a3;
--color-neutral-500: #737373;
--color-neutral-600: #525252;
--color-neutral-700: #404040;
--color-neutral-800: #262626;
--color-neutral-900: #171717;
--color-neutral-950: #0a0a0a;
--color-stone-50: #fafaf9;
--color-stone-100: #f5f5f4;
--color-stone-200: #e7e5e4;
--color-stone-300: #d6d3d1;
--color-stone-400: #a8a29e;
--color-stone-500: #78716c;
--color-stone-600: #57534e;
--color-stone-700: #44403c;
--color-stone-800: #292524;
--color-stone-900: #1c1917;
--color-stone-950: #0c0a09;
--color-red-50: #fef2f2;
--color-red-100: #fee2e2;
--color-red-200: #fecaca;
--color-red-300: #fca5a5;
--color-red-400: #f87171;
--color-red-500: #ef4444;
--color-red-600: #dc2626;
--color-red-700: #b91c1c;
--color-red-800: #991b1b;
--color-red-900: #7f1d1d;
--color-red-950: #450a0a;
--color-orange-50: #fff7ed;
--color-orange-100: #ffedd5;
--color-orange-200: #fed7aa;
--color-orange-300: #fdba74;
--color-orange-400: #fb923c;
--color-orange-500: #f97316;
--color-orange-600: #ea580c;
--color-orange-700: #c2410c;
--color-orange-800: #9a3412;
--color-orange-900: #7c2d12;
--color-orange-950: #431407;
--color-amber-50: #fffbeb;
--color-amber-100: #fef3c7;
--color-amber-200: #fde68a;
--color-amber-300: #fcd34d;
--color-amber-400: #fbbf24;
--color-amber-500: #f59e0b;
--color-amber-600: #d97706;
--color-amber-700: #b45309;
--color-amber-800: #92400e;
--color-amber-900: #78350f;
--color-amber-950: #451a03;
--color-yellow-50: #fefce8;
--color-yellow-100: #fef9c3;
--color-yellow-200: #fef08a;
--color-yellow-300: #fde047;
--color-yellow-400: #facc15;
--color-yellow-500: #eab308;
--color-yellow-600: #ca8a04;
--color-yellow-700: #a16207;
--color-yellow-800: #854d0e;
--color-yellow-900: #713f12;
--color-yellow-950: #422006;
--color-lime-50: #f7fee7;
--color-lime-100: #ecfccb;
--color-lime-200: #d9f99d;
--color-lime-300: #bef264;
--color-lime-400: #a3e635;
--color-lime-500: #84cc16;
--color-lime-600: #65a30d;
--color-lime-700: #4d7c0f;
--color-lime-800: #3f6212;
--color-lime-900: #365314;
--color-lime-950: #1a2e05;
--color-green-50: #f0fdf4;
--color-green-100: #dcfce7;
--color-green-200: #bbf7d0;
--color-green-300: #86efac;
--color-green-400: #4ade80;
--color-green-500: #22c55e;
--color-green-600: #16a34a;
--color-green-700: #15803d;
--color-green-800: #166534;
--color-green-900: #14532d;
--color-green-950: #052e16;
--color-emerald-50: #ecfdf5;
--color-emerald-100: #d1fae5;
--color-emerald-200: #a7f3d0;
--color-emerald-300: #6ee7b7;
--color-emerald-400: #34d399;
--color-emerald-500: #10b981;
--color-emerald-600: #059669;
--color-emerald-700: #047857;
--color-emerald-800: #065f46;
--color-emerald-900: #064e3b;
--color-emerald-950: #022c22;
--color-teal-50: #f0fdfa;
--color-teal-100: #ccfbf1;
--color-teal-200: #99f6e4;
--color-teal-300: #5eead4;
--color-teal-400: #2dd4bf;
--color-teal-500: #14b8a6;
--color-teal-600: #0d9488;
--color-teal-700: #0f766e;
--color-teal-800: #115e59;
--color-teal-900: #134e4a;
--color-teal-950: #042f2e;
--color-cyan-50: #ecfeff;
--color-cyan-100: #cffafe;
--color-cyan-200: #a5f3fc;
--color-cyan-300: #67e8f9;
--color-cyan-400: #22d3ee;
--color-cyan-500: #06b6d4;
--color-cyan-600: #0891b2;
--color-cyan-700: #0e7490;
--color-cyan-800: #155e75;
--color-cyan-900: #164e63;
--color-cyan-950: #083344;
--color-sky-50: #f0f9ff;
--color-sky-100: #e0f2fe;
--color-sky-200: #bae6fd;
--color-sky-300: #7dd3fc;
--color-sky-400: #38bdf8;
--color-sky-500: #0ea5e9;
--color-sky-600: #0284c7;
--color-sky-700: #0369a1;
--color-sky-800: #075985;
--color-sky-900: #0c4a6e;
--color-sky-950: #082f49;
--color-blue-50: #eff6ff;
--color-blue-100: #dbeafe;
--color-blue-200: #bfdbfe;
--color-blue-300: #93c5fd;
--color-blue-400: #60a5fa;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-blue-700: #1d4ed8;
--color-blue-800: #1e40af;
--color-blue-900: #1e3a8a;
--color-blue-950: #172554;
--color-indigo-50: #eef2ff;
--color-indigo-100: #e0e7ff;
--color-indigo-200: #c7d2fe;
--color-indigo-300: #a5b4fc;
--color-indigo-400: #818cf8;
--color-indigo-500: #6366f1;
--color-indigo-600: #4f46e5;
--color-indigo-700: #4338ca;
--color-indigo-800: #3730a3;
--color-indigo-900: #312e81;
--color-indigo-950: #1e1b4b;
--color-violet-50: #f5f3ff;
--color-violet-100: #ede9fe;
--color-violet-200: #ddd6fe;
--color-violet-300: #c4b5fd;
--color-violet-400: #a78bfa;
--color-violet-500: #8b5cf6;
--color-violet-600: #7c3aed;
--color-violet-700: #6d28d9;
--color-violet-800: #5b21b6;
--color-violet-900: #4c1d95;
--color-violet-950: #2e1065;
--color-purple-50: #faf5ff;
--color-purple-100: #f3e8ff;
--color-purple-200: #e9d5ff;
--color-purple-300: #d8b4fe;
--color-purple-400: #c084fc;
--color-purple-500: #a855f7;
--color-purple-600: #9333ea;
--color-purple-700: #7e22ce;
--color-purple-800: #6b21a8;
--color-purple-900: #581c87;
--color-purple-950: #3b0764;
--color-fuchsia-50: #fdf4ff;
--color-fuchsia-100: #fae8ff;
--color-fuchsia-200: #f5d0fe;
--color-fuchsia-300: #f0abfc;
--color-fuchsia-400: #e879f9;
--color-fuchsia-500: #d946ef;
--color-fuchsia-600: #c026d3;
--color-fuchsia-700: #a21caf;
--color-fuchsia-800: #86198f;
--color-fuchsia-900: #701a75;
--color-fuchsia-950: #4a044e;
--color-pink-50: #fdf2f8;
--color-pink-100: #fce7f3;
--color-pink-200: #fbcfe8;
--color-pink-300: #f9a8d4;
--color-pink-400: #f472b6;
--color-pink-500: #ec4899;
--color-pink-600: #db2777;
--color-pink-700: #be185d;
--color-pink-800: #9d174d;
--color-pink-900: #831843;
--color-pink-950: #500724;
--color-rose-50: #fff1f2;
--color-rose-100: #ffe4e6;
--color-rose-200: #fecdd3;
--color-rose-300: #fda4af;
--color-rose-400: #fb7185;
--color-rose-500: #f43f5e;
--color-rose-600: #e11d48;
--color-rose-700: #be123c;
--color-rose-800: #9f1239;
--color-rose-900: #881337;
--color-rose-950: #4c0519;
/* End: Colors from Tailwind CSS */
}

View file

@ -3,29 +3,111 @@ const internalData = {
stations: [], stations: [],
}; };
let geolocationPermission = false; let geolocationPermission = false;
let geolocation = [null, null]; let geolocation = null;
const openStations = new Set();
let sortByDistance = false;
const substituteData = [
Object.defineProperty(String.prototype, 'capitalize', { {
value: function () { name: 'Borgweg (Stadtpark)',
return this.charAt(0).toUpperCase() + this.toLowerCase().slice(1); coordinates: [53.5907696, 10.0147719],
}, },
enumerable: false {
}); name: 'Emilienstraße',
coordinates: [53.5716862, 9.9525424],
},
{
name: 'Garstedt',
coordinates: [53.6844739, 9.9860415],
},
{
name: 'Hagenbecks Tierpark',
coordinates: [53.5925874, 9.9440359],
},
{
name: 'Hamburg Hbf',
searchTarget: "Hauptbahnhof",
},
{
name: 'Jungfernstieg',
searchTarget: "Rathaus",
},
{
name: 'Rathaus',
searchTarget: "Jungfernstieg",
},
{
name: 'Stephansplatz (Oper/CCH)',
searchTarget: "Dammtor (Messe/CCH)",
},
{
name: 'Dammtor (Messe/CCH)',
searchTarget: "Stephansplatz (Oper/CCH)",
},
{
name: 'Hauptbahnhof Nord',
coordinates: [53.5541197, 10.0061270],
},
{
name: 'Hoheneichen',
coordinates: [53.6355141, 10.0677176],
},
{
name: 'Kornweg (Klein Borstel)',
coordinates: [53.6324430, 10.0541722],
},
{
name: 'Lauenbrück',
coordinates: [53.1971209, 9.5640765],
},
{
name: 'Lutterothstraße',
coordinates: [53.5819938, 9.9476215],
},
{
name: 'Meckelfeld',
coordinates: [53.4248897, 10.0291223],
},
{
name: 'Sengelmannstraße (City Nord)',
coordinates: [53.6093953, 10.0220004],
},
{
name: 'St.Pauli',
coordinates: [53.5507957, 9.9700752],
},
{
name: 'Winsen(Luhe)',
coordinates: [53.3534304, 10.2086841],
},
]
async function loadElevators() { async function loadElevators() {
const res = await fetch('https://www.hvv.de/elevators', {referrer: ""}); document.querySelector('#errorMessage').classList.add('hidden');
const res = await fetch('https://www.hvv.de/elevators', {
referrer: "",
}).catch(e => {
document.querySelector('#errorMessage').classList.remove('hidden');
});
const data = await res.json(); const data = await res.json();
const stations = data['stations']; const stations = data['stations'];
stations.sort(sortStations) stations.sort(sortStations);
internalData.lastUpdate = new Date(data['lastUpdate']); internalData.lastUpdate = new Date(data['lastUpdate']);
let stationIndex = 0; let stationIndex = 0;
for (const station of stations) { for (const station of stations) {
const stationName = station['mainSubStation']['stationName']; const stationName = station['mainSubStation']['stationName'];
const stationComment = station['mainSubStation']['comment'];
let searchTarget = undefined;
const substitute = substituteData.filter(subs => subs.name === stationName)
if (substitute.length && substitute[0].hasOwnProperty('searchTarget')) {
searchTarget = substitute[0].searchTarget;
}
const lines = new Set(); const lines = new Set();
const elevators = []; const elevators = [];
for (const elevatorKey of Object.keys(station.elevators)) { for (const elevatorKey of Object.keys(station.elevators)) {
@ -93,7 +175,8 @@ async function loadElevators() {
internalData.stations[stationIndex++] = { internalData.stations[stationIndex++] = {
name: stationName, name: stationName,
comment: station['comment'], comment: stationComment,
searchTarget: searchTarget,
state: stationState, state: stationState,
lines: stationLines, lines: stationLines,
types: stationTypes, types: stationTypes,
@ -101,15 +184,12 @@ async function loadElevators() {
} }
} }
localStorage.setItem("internal_data", JSON.stringify(internalData)); localStorage.setItem("internal_data", JSON.stringify({
api: minorVersion,
...internalData
}));
} }
const dateTimeStyle = new Intl.DateTimeFormat('de-DE', {
dateStyle: 'medium',
timeStyle: 'medium',
timeZone: 'Europe/Berlin',
})
async function loadOsmData() { async function loadOsmData() {
const nodeIdList = []; const nodeIdList = [];
for (const station of internalData.stations) { for (const station of internalData.stations) {
@ -118,12 +198,56 @@ async function loadOsmData() {
} }
} }
const osmData = await fetch(`https://overpass-api.de/api/interpreter?data=[out:json];node(id:${nodeIdList.join(',')});out%20body;`, {}); const osmResponse = await fetch(`https://overpass-api.de/api/interpreter?data=[out:json];node(id:${nodeIdList.join(',')});out%20body;`, {});
const osmJson = await osmData.json(); const osmJson = await osmResponse.json();
if (!osmJson.hasOwnProperty('elements')) return; if (!osmJson.hasOwnProperty('elements')) return;
localStorage.setItem("osm_data", JSON.stringify(osmJson)); const osmNodes = {};
for await (const node of osmJson.elements) {
if (node.hasOwnProperty('tags')) {
const tags = node['tags'];
if (tags['highway'] === 'elevator') {
osmNodes[node['id']] = node;
} else {
console.warn(`OSM Node is not an elevator. (NodeID: ${node['id']})`);
}
} else {
console.warn(`OSM Node has no Tags. (NodeID: ${node['id']})`);
}
}
//update coordinates in stations
for (const stationIndex in internalData.stations) {
const station = internalData.stations[stationIndex];
for (const elevator of station.elevators) {
const node = osmNodes[elevator.osmNodeId]
if (node) {
internalData.stations[stationIndex]['coordinates'] = [
node['lat'],
node['lon'],
]
}
}
if (!internalData.stations[stationIndex].hasOwnProperty('coordinates')) {
const substitute = substituteData.filter(subs => subs.name === internalData.stations[stationIndex].name)
console.log(substitute)
if (substitute.length && substitute[0].hasOwnProperty('coordinates')) {
internalData.stations[stationIndex]['coordinates'] = substitute[0].coordinates;
}
}
}
localStorage.setItem("osm_data", JSON.stringify({
api: minorVersion,
lastUpdate: new Date(),
nodes: osmNodes
}));
localStorage.setItem("internal_data", JSON.stringify({
api: minorVersion,
...internalData
}));
} }
function distance([lat1, lon1], [lat2, lon2]) { function distance([lat1, lon1], [lat2, lon2]) {
@ -144,9 +268,12 @@ function distance([lat1, lon1], [lat2, lon2]) {
function registerGeolocationWatcher() { function registerGeolocationWatcher() {
navigator.geolocation.watchPosition((pos) => { navigator.geolocation.watchPosition((pos) => {
geolocation = [pos.coords.latitude, pos.coords.longitude] if (geolocation === null) {
geolocation = [pos.coords.latitude, pos.coords.longitude];
renderData(geolocation);
}
}, (e) => { }, (e) => {
console.log(e) console.warn(e)
}, { }, {
enableHighAccuracy: true, enableHighAccuracy: true,
timeout: 5000, timeout: 5000,
@ -176,8 +303,6 @@ function allowGeolocation() {
checkGeolocationPermission() checkGeolocationPermission()
document.querySelector('#stationsNearMe').addEventListener('click', allowGeolocation)
function sortStations(stationA, stationB) { function sortStations(stationA, stationB) {
const nameA = stationA.mainSubStation.stationName.toUpperCase(); // ignore upper and lowercase const nameA = stationA.mainSubStation.stationName.toUpperCase(); // ignore upper and lowercase
const nameB = stationB.mainSubStation.stationName.toUpperCase(); // ignore upper and lowercase const nameB = stationB.mainSubStation.stationName.toUpperCase(); // ignore upper and lowercase
@ -192,6 +317,34 @@ function sortStations(stationA, stationB) {
return 0; return 0;
} }
function sortInt(valueA, valueB) {
const a = parseInt(valueA)
const b = parseInt(valueB)
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
// names must be equal
return 0;
}
function sortStationsByDistance(stationA, stationB) {
const distanceA = stationA.distance ?? 0; // ignore upper and lowercase
const distanceB = stationB.distance ?? 0; // ignore upper and lowercase
if (distanceA < distanceB) {
return -1;
}
if (distanceA > distanceB) {
return 1;
}
// names must be equal
return 0;
}
function getType(line) { function getType(line) {
const type = line.replace(/[^A-z]/g, ""); const type = line.replace(/[^A-z]/g, "");
switch (type) { switch (type) {
@ -217,7 +370,7 @@ function getTypes(lines) {
return types; return types;
} }
function renderData() { function renderData(location = null) {
const ls = JSON.parse(localStorage.getItem("internal_data")); const ls = JSON.parse(localStorage.getItem("internal_data"));
if (!ls) return; if (!ls) return;
internalData.lastUpdate = ls['lastUpdate']; internalData.lastUpdate = ls['lastUpdate'];
@ -229,28 +382,94 @@ function renderData() {
return; return;
} }
document.querySelector('#loadElevators').innerHTML = 'Daten aktualisieren'; if (
location !== null
&& (
location.length !== 2
|| typeof location[0] !== 'number'
|| typeof location[1] !== 'number'
)
) {
console.error('No valid location provided')
return;
}
document.querySelector('#updateInfo').classList.remove('hidden');
document.querySelector('#loadElevators').classList.remove('hidden');
document.querySelector('#filters').classList.remove('hidden');
document.querySelector('#initialLoad').classList.add('hidden');
const dateContainer = document.querySelector('#lastUpdated'); const dateContainer = document.querySelector('#lastUpdated');
dateContainer.innerHTML = dateTimeStyle.format(new Date(internalData.lastUpdate)); const oldDataWarning = document.querySelector('#oldDataWarning');
const lastUpdate = new Date(internalData.lastUpdate);
const now = new Date();
dateContainer.innerHTML = dateTimeStyle.format(lastUpdate);
oldDataWarning.classList.add('hidden');
if (now - lastUpdate > 86400 * 1000) {
const days = numberFormat.format((now - lastUpdate) / (86400 * 1000));
oldDataWarning.classList.remove('hidden');
oldDataWarning.innerHTML = `Daten ${days} Tag${days !== '1' ? 'e' : ''} alt!`;
}
const listContainer = document.querySelector('#stationList'); const listContainer = document.querySelector('#stationList');
//clear list before update //clear list before update
listContainer.innerHTML = ''; listContainer.innerHTML = '';
const stations = internalData['stations']; let stations = [...internalData['stations']];
for (const stationIndex in stations) { for (const stationIndex in stations) {
const station = stations[stationIndex]; const station = stations[stationIndex];
station.id = stationIndex;
if (location !== null) {
if (station.hasOwnProperty('coordinates')) {
station.distance = distance(location, station.coordinates);
} else {
console.log('station has no position:', station.name);
}
}
}
if (sortByDistance) {
stations = stations.sort(sortStationsByDistance);
}
for (const stationIndex in stations) {
const station = stations[stationIndex];
if (location !== null) {
if (station.hasOwnProperty('coordinates')) {
station.distance = distance(location, station.coordinates);
} else {
console.log('station has no position:', station.name);
}
}
let elevatorsTemplate = ''; let elevatorsTemplate = '';
let previewTemplate = ''; let previewTemplate = '';
for (const elevator of station.elevators) { for (const elevator of station.elevators) {
let linesTemplate = '<div class="lineList">Linien: '; const stateTemplate = `<span data-icon="${elevator.working || elevator.stateUnavailable ? 'elevator' : 'elevator-slash'}"
class="stateIcon size-xl ${elevator.working ? 'text-green' : elevator.stateUnavailable ? 'text-orange' : 'text-red'}"
role="img"
aria-label="${elevator.stateUnavailable
? 'Status nicht verfügbar.'
: elevator.working
? 'Funktionsfähig'
: 'Außer Betrieb'}">
</span>`;
let linesTemplate = '';
linesTemplate = `<div class="firstRow hiddenOnDesktop">${stateTemplate}`;
if (elevator.lines.length) {
linesTemplate = `<div class="firstRow">${stateTemplate}`;
linesTemplate += `<div class="lineList">Linien: `;
for (const line of elevator.lines) { for (const line of elevator.lines) {
linesTemplate += `<span data-type="${line.type}" data-line="${line.line}" class="lineChip">${line.line}</span>`; linesTemplate += `<span data-type="${line.type}" data-line="${line.line}" class="lineChip">${line.line}</span>`;
} }
linesTemplate += '</div>'; linesTemplate += '</div>';
}
linesTemplate += '</div>';
let levelsTemplate = '<ol class="levelList">'; let levelsTemplate = '<ol class="levelList">';
for (const levelDescription of elevator.levels) { for (const levelDescription of elevator.levels) {
@ -260,36 +479,32 @@ function renderData() {
let osmTemplate = ''; let osmTemplate = '';
if (osmData) { if (osmData) {
const nodes = osmData.elements.filter(node => node.id === elevator.osmNodeId) const node = osmData.nodes[elevator.osmNodeId]
if (nodes.length) { if (node) {
const nodeInfo = nodes[0];
if (nodeInfo.hasOwnProperty('tags')) {
const tags = nodeInfo['tags'];
if (tags['highway'] === 'elevator') {
osmTemplate = '<dl>'; osmTemplate = '<dl>';
osmTemplate += `<div><dt><span data-icon="location" class="size-l"></span>Link zur Karte</dt><dd><a href="https://www.openstreetmap.org/node/${elevator.osmNodeId}" target="_blank"> osmTemplate += `<div>
Auf Karte anzeigen <dt><span data-icon="location" class="size-l"></span>Link zur Karte</dt>
</a></dd></div>`; <dd>
if (tags.hasOwnProperty('description')) { <a href="https://www.openstreetmap.org/node/${elevator.osmNodeId}" target="_blank">
osmTemplate += `<div><dt><span data-icon="info" class="size-l"></span>Beschreibung</dt><dd>${tags['description']}</dd></div>`; Auf Karte anzeigen
</a>
</dd>
</div>`;
if (node.tags.hasOwnProperty('description')) {
osmTemplate += `<div><dt><span data-icon="info" class="size-l"></span>Beschreibung</dt><dd>${node.tags['description']}</dd></div>`;
} }
if (tags.hasOwnProperty('level')) { if (node.tags.hasOwnProperty('level')) {
osmTemplate += `<div><dt><span data-icon="up-down" class="size-l"></span>Ebenen</dt><dd>${tags['level'].split(';').sort().join(', ')}</dd></div>`; osmTemplate += `<div><dt><span data-icon="up-down" class="size-l"></span>Ebenen</dt><dd>${node.tags['level'].split(';').sort(sortInt).join(', ')}</dd></div>`;
} }
if (tags.hasOwnProperty('wheelchair')) { if (node.tags.hasOwnProperty('wheelchair')) {
osmTemplate += `<div><dt><span data-icon="wheelchair" class="size-l"></span>Rollstühle</dt><dd>${tags['wheelchair'] === 'yes' ? 'Ja' : 'Nein'}</dd></div>`; osmTemplate += `<div><dt><span data-icon="wheelchair" class="size-l"></span>Rollstühle</dt><dd>${node.tags['wheelchair'] === 'yes' ? 'Ja' : 'Nein'}</dd></div>`;
} }
if (tags.hasOwnProperty('bicycle')) { if (node.tags.hasOwnProperty('bicycle')) {
osmTemplate += `<div><dt><span data-icon="bicycle" class="size-l"></span>Fahrräder</dt><dd>${tags['bicycle'] === 'yes' ? 'Ja' : 'Nein'}</dd></div>`; osmTemplate += `<div><dt><span data-icon="bicycle" class="size-l"></span>Fahrräder</dt><dd>${node.tags['bicycle'] === 'yes' ? 'Ja' : 'Nein'}</dd></div>`;
} }
osmTemplate += '<dl>'; osmTemplate += '</dl>';
} else {
console.warn(`OSM Node is not an elevator. At:\t${station.name}\t${elevator.label} (NodeID: ${elevator.osmNodeId})`);
}
} else {
console.warn(`OSM Node has no Tags. At:\t${station.name}\t${elevator.label} (NodeID: ${elevator.osmNodeId})`);
}
} else { } else {
console.warn(`OSM Node not found (deleted). At:\t${station.name}\t${elevator.label} (NodeID: ${elevator.osmNodeId})`); console.warn(`OSM Node not found (deleted). At:\t${station.name}\t${elevator.label} (NodeID: ${elevator.osmNodeId})`);
} }
@ -301,19 +516,10 @@ Auf Karte anzeigen
previewTemplate += `<span data-icon="${elevator.working || elevator.stateUnavailable ? 'elevator' : 'elevator-slash'}" previewTemplate += `<span data-icon="${elevator.working || elevator.stateUnavailable ? 'elevator' : 'elevator-slash'}"
class="size-l ${elevator.working ? 'text-green' : elevator.stateUnavailable ? 'text-orange' : 'text-red'}"></span>` class="size-l ${elevator.working ? 'text-green' : elevator.stateUnavailable ? 'text-orange' : 'text-red'}"></span>`
elevatorsTemplate += `<li class="elevator"> elevatorsTemplate += `<li class="elevator">
<span data-icon="${elevator.working || elevator.stateUnavailable ? 'elevator' : 'elevator-slash'}" ${stateTemplate}
class="size-xl ${elevator.working ? 'text-green' : elevator.stateUnavailable ? 'text-orange' : 'text-red'}"
role="img"
aria-label="${elevator.stateUnavailable
? 'Status nicht verfügbar.'
: elevator.working
? 'Funktionsfähig'
: 'Außer Betrieb'}">
</span>
<div class="elevatorData"> <div class="elevatorData">
${elevator.lines.length ? `${linesTemplate}` : ''} ${linesTemplate}
${elevator.instCause !== '' ? `<div class="elevatorInfo">${elevator.instCause}</div>` : ''} ${elevator.instCause !== '' ? `<div class="elevatorInfo">${elevator.instCause}</div>` : ''}
${elevator.levels.length ? levelsTemplate : elevator.description} ${elevator.levels.length ? levelsTemplate : elevator.description}
<dl> <dl>
@ -340,18 +546,34 @@ Auf Karte anzeigen
</dl> </dl>
<!--<hr>--> <!--<hr>-->
${osmTemplate ? `<div class="osm" data-nodeid="${elevator.osmNodeId}"> ${osmTemplate ? `<div class="osm" data-nodeid="${elevator.osmNodeId}">
<div class="osmHeading">
<h4>Daten von OpenStreetMap</h4> <h4>Daten von OpenStreetMap</h4>
<button class="loadOSM size-s">
OSM Daten aktualisieren
<span data-icon="load" class="size-s spinner hidden"></span>
</button>
</div>
${osmTemplate} ${osmTemplate}
</div>` : ''} </div>` : `<button class="loadOSM">
Zusätzliche Daten von OpenStreetMap abrufen
<span data-icon="load" class="size-s spinner hidden"></span>
</button>`}
</div> </div>
</li>`; </li>`;
} }
const template = `<li class="station" id="station_${stationIndex}"> const template = `<li class="station" id="station_${station.id}">
<div class="stationSummary"> <div class="stationSummary">
<div class="symbol">
<div class="typeList"> <div class="typeList">
${station.types.sort().map(t => `<span class="typeChip" data-type="${t}">${t}</span>`).join('')} ${station.types.sort().map(t => `<span class="typeChip" data-type="${t}">${t}</span>`).join('')}
</div> </div>
${sortByDistance
? typeof station.distance !== 'undefined'
? `<div class="distance"><b>${Math.round(station.distance / 100) / 10}</b><br>km</div>`
: '<div class="distance">? km</div>'
: ''}
</div>
<div class="stationTitle"> <div class="stationTitle">
<h3>${station.name}</h3> <h3>${station.name}</h3>
<div class="elevator-preview" role="img" <div class="elevator-preview" role="img"
@ -362,52 +584,137 @@ ${station.state.unavailable ? `Bei ${station.state.unavailable} ${station.state.
</div> </div>
</div> </div>
</div> </div>
<details> <details data-stationid="${station.id}" ${openStations.has(station.id) ? 'open' : ''}>
<summary> <summary>
Aufzüge anzeigen <span>Details / Aufzüge anzeigen</span>
</summary> </summary>
<ul class="elevatorList collapsed" aria-expanded="false" id="station_${stationIndex}_elevatorList"> ${station.comment ? `<div class="comment"><p>${station.comment}</p></div>` : ''}
<ul class="elevatorList collapsed" aria-expanded="false" id="station_${station.id}_elevatorList">
${elevatorsTemplate} ${elevatorsTemplate}
</ul> </ul>
</details> </details>
</li>`; </li>`;
listContainer.insertAdjacentHTML('beforeend', template); listContainer.insertAdjacentHTML('beforeend', template);
// immediate invocation //immediate invocation
// (function () { (function () {
// const stationId = stationIndex; listContainer.querySelectorAll(`#station_${station.id} .loadOSM`).forEach(e => {
// listContainer.querySelector(`#station_${stationIndex}`) e.addEventListener('click', (ev) => {
// .addEventListener('click', () => toggleElevatorList(stationId)) ev.target.querySelector('.spinner').classList.remove('hidden');
// }()); loadOsmData().then(() => {
ev.target.classList.add('hidden');
renderData();
});
})
})
}());
} }
listContainer.insertAdjacentHTML('beforeend', `<li id="filterInfo">
<span id="filteredCount">0</span> von <span id="stationCount">0</span> Stationen durch Filter ausgeblendet.
</li>`);
listContainer.querySelectorAll(`details`).forEach(e => {
e.addEventListener("toggle", (event) => {
const stationId = event.target.dataset['stationid'];
if (event.target.open) {
openStations.add(stationId);
} else {
openStations.delete(stationId);
}
});
})
filterData();
} }
document.querySelector('#loadElevators') document.querySelector('#loadElevators')
.addEventListener('click', (e) => { .addEventListener('click', (e) => {
e.target.querySelector('.spinner').classList.remove('hidden');
loadElevators().then(() => { loadElevators().then(() => {
e.target.querySelector('.spinner').classList.add('hidden');
renderData(); renderData();
filterData();
}); });
}) })
document.querySelector('#loadOSM') document.querySelector('#initialLoad')
.addEventListener('click', (e) => { .addEventListener('click', (e) => {
loadOsmData().then(() => { e.target.querySelector('.spinner').classList.remove('hidden');
loadElevators().then(() => {
e.target.classList.add('hidden');
renderData(); renderData();
}); });
}) })
renderData(); document.querySelector('#loadOsm')
.addEventListener('click', (e) => {
e.target.querySelector('.spinner').classList.remove('hidden');
loadOsmData().then(() => {
e.target.querySelector('.spinner').classList.add('hidden');
renderData();
closeDialog('#dialog_osm');
});
})
document.querySelector('#stationsNearMe')
.addEventListener('click', async (e) => {
e.target.querySelector('.spinner').classList.remove('hidden');
if (!sortByDistance) {
if (JSON.parse(localStorage.getItem("osm_data")) === null) {
openDialog('#dialog_osm');
} else {
if (geolocationPermission !== 'granted') {
allowGeolocation();
} else {
sortByDistance = e.target.ariaPressed = true;
// If geolocation is already set.
// If not the location watcher will re-render our data.
if (geolocation !== null) {
renderData(geolocation)
}
}
}
} else {
sortByDistance = e.target.ariaPressed = false;
renderData();
}
e.target.querySelector('.spinner').classList.add('hidden');
})
function filterData() { function filterData() {
const searchString = document.querySelector('#searchStation').value; const searchString = document.querySelector('#searchStation').value;
const typeU = document.querySelector('button.typeChip[data-type="U"]');
const typeS = document.querySelector('button.typeChip[data-type="S"]');
const typeA = document.querySelector('button.typeChip[data-type="A"]');
const typeR = document.querySelector('button.typeChip[data-type="R"]');
const activeTypes = [];
if (typeU.dataset.pressed === 'true') activeTypes.push('U');
if (typeS.dataset.pressed === 'true') activeTypes.push('S');
if (typeA.dataset.pressed === 'true') activeTypes.push('A');
if (typeR.dataset.pressed === 'true') activeTypes.push('R');
const stationCount = internalData.stations.length;
let filteredStations = 0;
if (internalData) { if (internalData) {
for (const stationIndex in internalData.stations) { for (const stationIndex in internalData.stations) {
const matches = internalData.stations[stationIndex].name.toLowerCase().search(searchString.toLowerCase()) >= 0; const matchesName = internalData.stations[stationIndex].name.toLowerCase().search(searchString.toLowerCase()) >= 0;
document.querySelector(`#station_${stationIndex}`).classList.toggle('hidden', !matches); const matchesSearchTarget = internalData.stations[stationIndex].hasOwnProperty('searchTarget')
? internalData.stations[stationIndex].searchTarget.toLowerCase().search(searchString.toLowerCase()) >= 0
: false;
let matchesType = false;
internalData.stations[stationIndex].types.forEach(type => {
if (activeTypes.includes(type)) matchesType = true;
})
const filtered = !((matchesName || matchesSearchTarget) && matchesType);
document.querySelector(`#station_${stationIndex}`).classList.toggle('hidden', filtered);
if (filtered) filteredStations++;
} }
document.querySelector('#stationCount').innerHTML = stationCount;
document.querySelector('#filteredCount').innerHTML = filteredStations;
} }
} }
@ -415,4 +722,24 @@ document.querySelector('#searchStation').addEventListener('input', (e) => {
filterData(); filterData();
}) })
filterData() document.querySelectorAll('button.typeChip').forEach(e => {
e.addEventListener('click', (event) => {
e.ariaPressed = e.dataset.pressed = e.dataset.pressed === 'true' ? 'false' : 'true';
filterData();
})
})
// data api version check
const check_internal = JSON.parse(localStorage.getItem("internal_data"));
const check_osm = JSON.parse(localStorage.getItem("osm_data"));
if (check_internal === null || check_internal.hasOwnProperty('api') && check_internal.api === minorVersion) {
if (check_osm === null || check_osm.hasOwnProperty('api') && check_osm.api === minorVersion) {
renderData();
} else {
console.log('osm_data: version mismatch')
localStorage.removeItem('osm_data');
}
} else {
console.log('internal_data: version mismatch')
localStorage.removeItem('internal_data');
}

BIN
icons/192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
icons/512-maskable.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
icons/512-transparent.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
icons/512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.3 (1:1.3+202307231459+0e150ed6c4)"
sodipodi:docname="favicon_source.svg"
inkscape:export-filename="favicon-transparent.png"
inkscape:export-xdpi="130.048"
inkscape:export-ydpi="130.048"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.9145201"
inkscape:cx="549.46851"
inkscape:cy="179.87576"
inkscape:window-width="1920"
inkscape:window-height="1085"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"><inkscape:page
x="0"
y="0"
width="100"
height="100"
id="page3"
margin="0"
bleed="0" /></sodipodi:namedview><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-104.49603,0.25552651)"><rect
style="fill:#65a30d;fill-opacity:1;stroke:none;stroke-width:0.499507;stroke-linejoin:round;paint-order:markers stroke fill"
id="rect1-1"
width="100"
height="100.00012"
x="104.49603"
y="-0.25558716" /><path
d="m 142.75747,64.972903 h 7.61422 V 54.820616 h 2.53804 V 48.47544 q 0,-2.093907 -1.49109,-3.585027 -1.49109,-1.49111 -3.58499,-1.49111 h -2.53813 q -2.0939,0 -3.58499,1.49111 -1.49109,1.49112 -1.49109,3.585027 v 6.345176 h 2.53803 z m 3.80716,-24.111679 q 1.33243,0 2.25246,-0.920037 0.91995,-0.920037 0.91995,-2.252542 0,-1.332485 -0.91995,-2.252532 -0.91993,-0.920036 -2.25246,-0.920036 -1.33252,0 -2.25256,0.920036 -0.91994,0.920037 -0.91994,2.252532 0,1.332495 0.91994,2.252542 0.91994,0.920037 2.25256,0.920037 z m 9.96184,6.345176 h 12.6904 l -6.3452,-10.152274 z m 6.3452,15.228432 6.3452,-10.152284 h -12.6904 z m -25.38069,12.309636 q -3.36291,0 -5.67889,-2.315986 -2.31606,-2.315986 -2.31606,-5.678936 V 32.7394 q 0,-3.362945 2.31606,-5.678933 2.31598,-2.315988 5.67889,-2.315988 h 34.01019 q 3.36292,0 5.67889,2.315988 2.31597,2.315988 2.31597,5.678933 v 34.010146 q 0,3.36295 -2.31597,5.678936 -2.31597,2.315986 -5.67889,2.315986 z"
id="path1-3-2"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0634518" /></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.3 (1:1.3+202307231459+0e150ed6c4)"
sodipodi:docname="favicon_source.svg"
inkscape:export-filename="favicon-transparent.png"
inkscape:export-xdpi="130.048"
inkscape:export-ydpi="130.048"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.9145201"
inkscape:cx="549.46851"
inkscape:cy="179.87576"
inkscape:window-width="1920"
inkscape:window-height="1085"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"><inkscape:page
x="0"
y="0"
width="100"
height="100"
id="page4"
inkscape:export-filename="favicon-maskable.svg"
inkscape:export-xdpi="130.048"
inkscape:export-ydpi="130.048"
margin="0"
bleed="0" /></sodipodi:namedview><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-214.49603)"><path
d="m 249.23591,69.79696 h 9.89848 V 56.598986 h 3.29945 v -8.248729 q 0,-2.722078 -1.93842,-4.660534 -1.93841,-1.938444 -4.66048,-1.938444 h -3.29958 q -2.72207,0 -4.66048,1.938444 -1.93842,1.938456 -1.93842,4.660534 v 8.248729 h 3.29945 z m 4.9493,-31.345184 q 1.73216,0 2.9282,-1.196048 1.19593,-1.196047 1.19593,-2.928304 0,-1.73223 -1.19593,-2.928291 -1.19591,-1.196048 -2.9282,-1.196048 -1.73228,0 -2.92832,1.196048 -1.19593,1.196048 -1.19593,2.928291 0,1.732244 1.19593,2.928304 1.19591,1.196048 2.92832,1.196048 z m 12.9504,8.24873 h 16.49751 l -8.24876,-13.197957 z m 8.24875,19.796961 8.24876,-13.19797 h -16.49751 z m -32.9949,16.002526 q -4.37178,0 -7.38255,-3.010781 -3.01088,-3.010782 -3.01088,-7.382617 V 27.893406 q 0,-4.37183 3.01088,-7.382614 3.01077,-3.010784 7.38255,-3.010784 h 44.21326 q 4.37179,0 7.38255,3.010784 3.01076,3.010784 3.01076,7.382614 v 44.213189 q 0,4.371835 -3.01076,7.382617 -3.01076,3.010781 -7.38255,3.010781 z"
id="path1-3"
style="fill:#000000;fill-opacity:1;stroke-width:0.0824873" /></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

58
icons/favicon.svg Normal file
View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.3 (1:1.3+202307231459+0e150ed6c4)"
sodipodi:docname="favicon_source.svg"
inkscape:export-filename="favicon-transparent.png"
inkscape:export-xdpi="130.048"
inkscape:export-ydpi="130.048"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.9145201"
inkscape:cx="549.46851"
inkscape:cy="179.87576"
inkscape:window-width="1920"
inkscape:window-height="1085"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"><inkscape:page
x="0"
y="0"
width="100"
height="100"
id="page2"
margin="0"
bleed="0" /></sodipodi:namedview><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><rect
style="fill:#65a30d;fill-opacity:1;stroke:#ffffff;stroke-width:9.49992e-05;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:stroke fill markers"
id="rect1"
width="94.999908"
height="94.999908"
x="2.5000463"
y="2.5000463"
ry="31.407814" /><path
d="m 34.739876,69.796959 h 9.898488 V 56.598987 h 3.299445 v -8.248731 q 0,-2.722078 -1.93842,-4.660533 -1.938411,-1.938445 -4.660481,-1.938445 H 38.03933 q -2.72207,0 -4.66048,1.938445 -1.93842,1.938455 -1.93842,4.660533 v 8.248731 h 3.299446 z M 39.68918,38.451776 q 1.732156,0 2.928204,-1.196048 1.195925,-1.196048 1.195925,-2.928304 0,-1.732231 -1.195925,-2.928292 -1.195916,-1.196048 -2.928204,-1.196048 -1.732278,0 -2.928325,1.196048 -1.195925,1.196049 -1.195925,2.928292 0,1.732244 1.195925,2.928304 1.195916,1.196048 2.928325,1.196048 z m 12.950396,8.24873 H 69.137087 L 60.888325,33.502549 Z m 8.248749,19.796962 8.248762,-13.197971 H 52.639576 Z M 27.893434,82.499994 q -4.371788,0 -7.382551,-3.010781 Q 17.5,76.478431 17.5,72.106595 V 27.893406 q 0,-4.37183 3.010883,-7.382615 3.010763,-3.010784 7.382551,-3.010784 h 44.213252 q 4.371798,0 7.382553,3.010784 Q 82.5,23.521576 82.5,27.893406 v 44.213189 q 0,4.371836 -3.010761,7.382618 -3.010755,3.010781 -7.382553,3.010781 z"
id="path1-3-7"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0824873" /></g></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

85
icons/favicon_source.svg Normal file
View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.3 (1:1.3+202307231459+0e150ed6c4)"
sodipodi:docname="favicon_source.svg"
inkscape:export-filename="favicon-transparent.png"
inkscape:export-xdpi="130.048"
inkscape:export-ydpi="130.048"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.9145201"
inkscape:cx="549.46851"
inkscape:cy="179.87576"
inkscape:window-width="1920"
inkscape:window-height="1085"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"><inkscape:page
x="0"
y="0"
width="100"
height="100"
id="page2"
margin="0"
bleed="0" /><inkscape:page
x="104.49603"
y="-0.25552651"
width="100"
height="100"
id="page3"
margin="0"
bleed="0" /><inkscape:page
x="214.49603"
y="0"
width="100"
height="100"
id="page4"
inkscape:export-filename="favicon-transparent.svg"
inkscape:export-xdpi="130.048"
inkscape:export-ydpi="130.048" /></sodipodi:namedview><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><rect
style="fill:#65a30d;fill-opacity:1;stroke:#ffffff;stroke-width:9.49992e-05;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:stroke fill markers"
id="rect1"
width="94.999908"
height="94.999908"
x="2.5000463"
y="2.5000463"
ry="31.407814" /><rect
style="fill:#65a30d;fill-opacity:1;stroke:none;stroke-width:0.499507;stroke-linejoin:round;paint-order:markers stroke fill"
id="rect1-1"
width="100"
height="100.00012"
x="104.49603"
y="-0.25558716" /><path
d="m 249.23591,69.79696 h 9.89848 V 56.598986 h 3.29945 v -8.248729 q 0,-2.722078 -1.93842,-4.660534 -1.93841,-1.938444 -4.66048,-1.938444 h -3.29958 q -2.72207,0 -4.66048,1.938444 -1.93842,1.938456 -1.93842,4.660534 v 8.248729 h 3.29945 z m 4.9493,-31.345184 q 1.73216,0 2.9282,-1.196048 1.19593,-1.196047 1.19593,-2.928304 0,-1.73223 -1.19593,-2.928291 -1.19591,-1.196048 -2.9282,-1.196048 -1.73228,0 -2.92832,1.196048 -1.19593,1.196048 -1.19593,2.928291 0,1.732244 1.19593,2.928304 1.19591,1.196048 2.92832,1.196048 z m 12.9504,8.24873 h 16.49751 l -8.24876,-13.197957 z m 8.24875,19.796961 8.24876,-13.19797 h -16.49751 z m -32.9949,16.002526 q -4.37178,0 -7.38255,-3.010781 -3.01088,-3.010782 -3.01088,-7.382617 V 27.893406 q 0,-4.37183 3.01088,-7.382614 3.01077,-3.010784 7.38255,-3.010784 h 44.21326 q 4.37179,0 7.38255,3.010784 3.01076,3.010784 3.01076,7.382614 v 44.213189 q 0,4.371835 -3.01076,7.382617 -3.01076,3.010781 -7.38255,3.010781 z"
id="path1-3"
style="fill:#000000;fill-opacity:1;stroke-width:0.0824873" /><path
d="m 34.739876,69.796959 h 9.898488 V 56.598987 h 3.299445 v -8.248731 q 0,-2.722078 -1.93842,-4.660533 -1.938411,-1.938445 -4.660481,-1.938445 H 38.03933 q -2.72207,0 -4.66048,1.938445 -1.93842,1.938455 -1.93842,4.660533 v 8.248731 h 3.299446 z M 39.68918,38.451776 q 1.732156,0 2.928204,-1.196048 1.195925,-1.196048 1.195925,-2.928304 0,-1.732231 -1.195925,-2.928292 -1.195916,-1.196048 -2.928204,-1.196048 -1.732278,0 -2.928325,1.196048 -1.195925,1.196049 -1.195925,2.928292 0,1.732244 1.195925,2.928304 1.195916,1.196048 2.928325,1.196048 z m 12.950396,8.24873 H 69.137087 L 60.888325,33.502549 Z m 8.248749,19.796962 8.248762,-13.197971 H 52.639576 Z M 27.893434,82.499994 q -4.371788,0 -7.382551,-3.010781 Q 17.5,76.478431 17.5,72.106595 V 27.893406 q 0,-4.37183 3.010883,-7.382615 3.010763,-3.010784 7.382551,-3.010784 h 44.213252 q 4.371798,0 7.382553,3.010784 Q 82.5,23.521576 82.5,27.893406 v 44.213189 q 0,4.371836 -3.010761,7.382618 -3.010755,3.010781 -7.382553,3.010781 z"
id="path1-3-7"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0824873" /><path
d="m 142.75747,64.972903 h 7.61422 V 54.820616 h 2.53804 V 48.47544 q 0,-2.093907 -1.49109,-3.585027 -1.49109,-1.49111 -3.58499,-1.49111 h -2.53813 q -2.0939,0 -3.58499,1.49111 -1.49109,1.49112 -1.49109,3.585027 v 6.345176 h 2.53803 z m 3.80716,-24.111679 q 1.33243,0 2.25246,-0.920037 0.91995,-0.920037 0.91995,-2.252542 0,-1.332485 -0.91995,-2.252532 -0.91993,-0.920036 -2.25246,-0.920036 -1.33252,0 -2.25256,0.920036 -0.91994,0.920037 -0.91994,2.252532 0,1.332495 0.91994,2.252542 0.91994,0.920037 2.25256,0.920037 z m 9.96184,6.345176 h 12.6904 l -6.3452,-10.152274 z m 6.3452,15.228432 6.3452,-10.152284 h -12.6904 z m -25.38069,12.309636 q -3.36291,0 -5.67889,-2.315986 -2.31606,-2.315986 -2.31606,-5.678936 V 32.7394 q 0,-3.362945 2.31606,-5.678933 2.31598,-2.315988 5.67889,-2.315988 h 34.01019 q 3.36292,0 5.67889,2.315988 2.31597,2.315988 2.31597,5.678933 v 34.010146 q 0,3.36295 -2.31597,5.678936 -2.31597,2.315986 -5.67889,2.315986 z"
id="path1-3-2"
style="fill:#ffffff;stroke-width:0.0634518;fill-opacity:1" /></g></svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View file

@ -6,44 +6,96 @@
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="style.css" rel="stylesheet"> <link href="style.css" rel="stylesheet">
<title>A11y Elevator List</title> <link rel="icon" type="image/png" sizes="512" href="/icons/512.png">
<link rel="icon" type="image/png" sizes="192" href="/icons/192.png">
<link rel="shortcut icon" href="/icons/favicon.svg">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/icons/favicon-transparent.svg" color="#65a30d">
<title>hvvstuhl.de</title>
</head> </head>
<body> <body>
<h1>A11y Elevator List</h1> <h1>Barrierefreie Aufzugs-Liste</h1>
<nav> <nav>
<ul> <ul>
<li> <li>
<a href="index.html"> <a href="index.html">
Elevator List Aufzugs-Liste
</a> </a>
</li> </li>
<li> <li>
<a href="about.html"> <a href="about.html">
What is this? Was ist das?
</a> </a>
</li> </li>
</ul> </ul>
</nav> </nav>
<main> <main>
<h2>Station List</h2> <h2>Liste aller Stationen mit Aufzug im HVV</h2>
<div> <div id="updateInfo" class="hidden">
<span>Last Updated: <b id="lastUpdated"></b></span> <span>Stand: <b id="lastUpdated"></b></span>
<button id="loadElevators">Daten vom HVV abrufen</button> <span id="oldDataWarning" class="hidden"></span>
<button id="loadOSM">OSM Daten abrufen</button> <button id="loadElevators" class="hidden">
</div> Aktualisieren
<br> <span data-icon="load" class="size-s spinner hidden"></span>
<div class="filters">
<input placeholder="Station suchen" id="searchStation">
<button id="stationsNearMe">
<span data-icon="location-searching" class="size-m"></span>
Stationen in der Nähe
</button> </button>
</div> </div>
<div class="hidden" id="filters">
<input placeholder="Station suchen" id="searchStation">
<button id="stationsNearMe" aria-pressed="false">
<span data-icon="location-searching" class="size-m"></span>
Standort
<span data-icon="load" class="size-m spinner hidden"></span>
</button>
<div id="typeFilter">
<button class="typeChip" data-type="U" data-pressed="true" aria-pressed="true">U</button>
<button class="typeChip" data-type="S" data-pressed="true" aria-pressed="true">S</button>
<button class="typeChip" data-type="A" data-pressed="true" aria-pressed="true">A</button>
<button class="typeChip" data-type="R" data-pressed="true" aria-pressed="true">R</button>
</div>
</div>
<button id="initialLoad">
Daten vom HVV abrufen
<span data-icon="load" class="size-m spinner hidden"></span>
</button>
<div class="hidden" id="errorMessage">
Leider ist ein Fehler beim Abrufen der Daten aufgetreten.
</div>
<ul id="stationList"> <ul id="stationList">
</ul> </ul>
</main> </main>
<footer>
<p>
hvvstuhl <span id="version"></span> &bull;
Entwickelt von <a href="https://kritzl.dev/">kritzl</a> & <a href="https://traumweh.dev/">traumweh</a> &bull;
Betrieben von <a href="https://mafiasi.de/">mafiasi</a> &bull;
<a href="https://mafiasi.de/base/imprint">Impressum</a>
</p>
</footer>
<div id="dialog_layer" class="dialogs">
<div role="dialog" id="dialog_osm" aria-labelledby="dialog_osm_label" aria-modal="true" class="">
<h2 id="dialog_osm_label">
Fehlende Daten abrufen
</h2>
<button onclick="closeDialog('#dialog_osm')" class="close-modal">
<span data-icon="close" class="size-m"></span>
</button>
<p>
Um die Stationen nach Entfernung sortieren zu können, müssen zusätzliche Daten von OpenStreetMap geladen
werden.
</p>
<button id="loadOsm">
Zusätzliche Daten von OpenStreetMap abrufen
<span data-icon="load" class="size-m spinner hidden"></span>
</button>
</div>
</div>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="elevators.js"></script> <script type="text/javascript" src="elevators.js"></script>
</body> </body>
</html> </html>

51
main.js Normal file
View file

@ -0,0 +1,51 @@
const version = '0.6.4'
const minorVersion = version.split('.').splice(0, 2).join('.');
const numberFormat = new Intl.NumberFormat('de-DE', {
maximumFractionDigits: 1
});
Object.defineProperty(String.prototype, 'capitalize', {
value: function () {
return this.charAt(0).toUpperCase() + this.toLowerCase().slice(1);
},
enumerable: false
});
const dateTimeStyle = new Intl.DateTimeFormat('de-DE', {
dateStyle: 'medium',
timeStyle: 'medium',
timeZone: 'Europe/Berlin',
})
// set version
document.querySelector('#version').innerHTML = `v${version}`;
function openDialog(selector) {
document.querySelector('body').classList.add('has-dialog')
document.querySelector('#dialog_layer').classList.add('active')
document.querySelector(selector).classList.remove('hidden')
}
function closeDialog(selector) {
document.querySelector('body').classList.remove('has-dialog')
document.querySelector('#dialog_layer').classList.remove('active')
document.querySelector(selector).classList.add('hidden')
}
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js")
.then((registration) => {
if (registration.installing) {
console.log('New Service Worker is installing...');
} else if (registration.waiting) {
console.log('Installed new service worker. Waiting for Update (up to 24h).');
} else if (registration.active) {
console.log('Service Worker is up to date.');
}
})
.catch((error) => {
// registration failed
console.error(`Registration failed with ${error}`);
});
}

49
manifest.json Normal file
View file

@ -0,0 +1,49 @@
{
"name": "hvvstuhl.de",
"short_name": "hvvstuhl",
"start_url": ".",
"display": "standalone",
"background_color": "#030712",
"theme_color": "#65a30d",
"description": "Barrierefreie Aufzugs-Liste",
"icons": [
{
"src": "/icons/192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/512-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/icons/512-transparent.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "monochrome"
}
],
"screenshots" : [
{
"src": "images/screenshot-mobile.png",
"sizes": "430x900",
"type": "image/png",
"form_factor": "narrow",
"label": "Stationsliste"
},
{
"src": "images/screenshot-desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide",
"label": "Stationsliste"
}
]
}

1
md_icons/close.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m256-168-88-88 224-224-224-224 88-88 224 224 224-224 88 88-224 224 224 224-88 88-224-224-224 224Z"/></svg>

After

Width:  |  Height:  |  Size: 222 B

1
md_icons/load.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M202-279q-32-45-48.5-96T137-480q0-140 98-241.5T470-823h8l-49-49 67-66 178 178-178 178-67-66 49-49h-5q-87 0-148.5 63.5T263-480q0 29 7.5 56.5T293-370l-91 91ZM464-21 286-199l178-179 67 67-49 49h5q87 0 148.5-64T697-480q0-29-7.5-56.5T667-590l91-90q32 45 48.5 95.5T823-480q0 140-98 242T490-136h-8l49 48-67 67Z"/></svg>

After

Width:  |  Height:  |  Size: 428 B

721
style.css
View file

@ -1,31 +1,307 @@
:root { :root {
--color-gray-50: hsl(60, 9%, 98%); /* Colors from Tailwind CSS: https://github.com/tailwindlabs/tailwindcss
--color-gray-100: hsl(60, 5%, 96%);
--color-gray-200: hsl(20, 6%, 90%); MIT License
--color-gray-300: hsl(24, 6%, 83%);
--color-gray-400: hsl(24, 5%, 64%); Copyright (c) Tailwind Labs, Inc.
--color-gray-500: hsl(25, 5%, 45%);
--color-gray-600: hsl(33, 5%, 32%); Permission is hereby granted, free of charge, to any person obtaining a copy
--color-gray-700: hsl(30, 6%, 25%); of this software and associated documentation files (the "Software"), to deal
--color-gray-800: hsl(12, 6%, 15%); in the Software without restriction, including without limitation the rights
--color-gray-900: hsl(24, 10%, 10%); to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--color-gray-950: hsl(20, 14%, 4%); copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
--color-black: #000;
--color-white: #fff;
--color-slate-50: #f8fafc;
--color-slate-100: #f1f5f9;
--color-slate-200: #e2e8f0;
--color-slate-300: #cbd5e1;
--color-slate-400: #94a3b8;
--color-slate-500: #64748b;
--color-slate-600: #475569;
--color-slate-700: #334155;
--color-slate-800: #1e293b;
--color-slate-900: #0f172a;
--color-slate-950: #020617;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
--color-gray-950: #030712;
--color-zinc-50: #fafafa;
--color-zinc-100: #f4f4f5;
--color-zinc-200: #e4e4e7;
--color-zinc-300: #d4d4d8;
--color-zinc-400: #a1a1aa;
--color-zinc-500: #71717a;
--color-zinc-600: #52525b;
--color-zinc-700: #3f3f46;
--color-zinc-800: #27272a;
--color-zinc-900: #18181b;
--color-zinc-950: #09090b;
--color-neutral-50: #fafafa;
--color-neutral-100: #f5f5f5;
--color-neutral-200: #e5e5e5;
--color-neutral-300: #d4d4d4;
--color-neutral-400: #a3a3a3;
--color-neutral-500: #737373;
--color-neutral-600: #525252;
--color-neutral-700: #404040;
--color-neutral-800: #262626;
--color-neutral-900: #171717;
--color-neutral-950: #0a0a0a;
--color-stone-50: #fafaf9;
--color-stone-100: #f5f5f4;
--color-stone-200: #e7e5e4;
--color-stone-300: #d6d3d1;
--color-stone-400: #a8a29e;
--color-stone-500: #78716c;
--color-stone-600: #57534e;
--color-stone-700: #44403c;
--color-stone-800: #292524;
--color-stone-900: #1c1917;
--color-stone-950: #0c0a09;
--color-red-50: #fef2f2;
--color-red-100: #fee2e2;
--color-red-200: #fecaca;
--color-red-300: #fca5a5;
--color-red-400: #f87171;
--color-red-500: #ef4444;
--color-red-600: #dc2626;
--color-red-700: #b91c1c;
--color-red-800: #991b1b;
--color-red-900: #7f1d1d;
--color-red-950: #450a0a;
--color-orange-50: #fff7ed;
--color-orange-100: #ffedd5;
--color-orange-200: #fed7aa;
--color-orange-300: #fdba74;
--color-orange-400: #fb923c;
--color-orange-500: #f97316;
--color-orange-600: #ea580c;
--color-orange-700: #c2410c;
--color-orange-800: #9a3412;
--color-orange-900: #7c2d12;
--color-orange-950: #431407;
--color-amber-50: #fffbeb;
--color-amber-100: #fef3c7;
--color-amber-200: #fde68a;
--color-amber-300: #fcd34d;
--color-amber-400: #fbbf24;
--color-amber-500: #f59e0b;
--color-amber-600: #d97706;
--color-amber-700: #b45309;
--color-amber-800: #92400e;
--color-amber-900: #78350f;
--color-amber-950: #451a03;
--color-yellow-50: #fefce8;
--color-yellow-100: #fef9c3;
--color-yellow-200: #fef08a;
--color-yellow-300: #fde047;
--color-yellow-400: #facc15;
--color-yellow-500: #eab308;
--color-yellow-600: #ca8a04;
--color-yellow-700: #a16207;
--color-yellow-800: #854d0e;
--color-yellow-900: #713f12;
--color-yellow-950: #422006;
--color-lime-50: #f7fee7;
--color-lime-100: #ecfccb;
--color-lime-200: #d9f99d;
--color-lime-300: #bef264;
--color-lime-400: #a3e635;
--color-lime-500: #84cc16;
--color-lime-600: #65a30d;
--color-lime-700: #4d7c0f;
--color-lime-800: #3f6212;
--color-lime-900: #365314;
--color-lime-950: #1a2e05;
--color-green-50: #f0fdf4;
--color-green-100: #dcfce7;
--color-green-200: #bbf7d0;
--color-green-300: #86efac;
--color-green-400: #4ade80;
--color-green-500: #22c55e;
--color-green-600: #16a34a;
--color-green-700: #15803d;
--color-green-800: #166534;
--color-green-900: #14532d;
--color-green-950: #052e16;
--color-emerald-50: #ecfdf5;
--color-emerald-100: #d1fae5;
--color-emerald-200: #a7f3d0;
--color-emerald-300: #6ee7b7;
--color-emerald-400: #34d399;
--color-emerald-500: #10b981;
--color-emerald-600: #059669;
--color-emerald-700: #047857;
--color-emerald-800: #065f46;
--color-emerald-900: #064e3b;
--color-emerald-950: #022c22;
--color-teal-50: #f0fdfa;
--color-teal-100: #ccfbf1;
--color-teal-200: #99f6e4;
--color-teal-300: #5eead4;
--color-teal-400: #2dd4bf;
--color-teal-500: #14b8a6;
--color-teal-600: #0d9488;
--color-teal-700: #0f766e;
--color-teal-800: #115e59;
--color-teal-900: #134e4a;
--color-teal-950: #042f2e;
--color-cyan-50: #ecfeff;
--color-cyan-100: #cffafe;
--color-cyan-200: #a5f3fc;
--color-cyan-300: #67e8f9;
--color-cyan-400: #22d3ee;
--color-cyan-500: #06b6d4;
--color-cyan-600: #0891b2;
--color-cyan-700: #0e7490;
--color-cyan-800: #155e75;
--color-cyan-900: #164e63;
--color-cyan-950: #083344;
--color-sky-50: #f0f9ff;
--color-sky-100: #e0f2fe;
--color-sky-200: #bae6fd;
--color-sky-300: #7dd3fc;
--color-sky-400: #38bdf8;
--color-sky-500: #0ea5e9;
--color-sky-600: #0284c7;
--color-sky-700: #0369a1;
--color-sky-800: #075985;
--color-sky-900: #0c4a6e;
--color-sky-950: #082f49;
--color-blue-50: #eff6ff;
--color-blue-100: #dbeafe;
--color-blue-200: #bfdbfe;
--color-blue-300: #93c5fd;
--color-blue-400: #60a5fa;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-blue-700: #1d4ed8;
--color-blue-800: #1e40af;
--color-blue-900: #1e3a8a;
--color-blue-950: #172554;
--color-indigo-50: #eef2ff;
--color-indigo-100: #e0e7ff;
--color-indigo-200: #c7d2fe;
--color-indigo-300: #a5b4fc;
--color-indigo-400: #818cf8;
--color-indigo-500: #6366f1;
--color-indigo-600: #4f46e5;
--color-indigo-700: #4338ca;
--color-indigo-800: #3730a3;
--color-indigo-900: #312e81;
--color-indigo-950: #1e1b4b;
--color-violet-50: #f5f3ff;
--color-violet-100: #ede9fe;
--color-violet-200: #ddd6fe;
--color-violet-300: #c4b5fd;
--color-violet-400: #a78bfa;
--color-violet-500: #8b5cf6;
--color-violet-600: #7c3aed;
--color-violet-700: #6d28d9;
--color-violet-800: #5b21b6;
--color-violet-900: #4c1d95;
--color-violet-950: #2e1065;
--color-purple-50: #faf5ff;
--color-purple-100: #f3e8ff;
--color-purple-200: #e9d5ff;
--color-purple-300: #d8b4fe;
--color-purple-400: #c084fc;
--color-purple-500: #a855f7;
--color-purple-600: #9333ea;
--color-purple-700: #7e22ce;
--color-purple-800: #6b21a8;
--color-purple-900: #581c87;
--color-purple-950: #3b0764;
--color-fuchsia-50: #fdf4ff;
--color-fuchsia-100: #fae8ff;
--color-fuchsia-200: #f5d0fe;
--color-fuchsia-300: #f0abfc;
--color-fuchsia-400: #e879f9;
--color-fuchsia-500: #d946ef;
--color-fuchsia-600: #c026d3;
--color-fuchsia-700: #a21caf;
--color-fuchsia-800: #86198f;
--color-fuchsia-900: #701a75;
--color-fuchsia-950: #4a044e;
--color-pink-50: #fdf2f8;
--color-pink-100: #fce7f3;
--color-pink-200: #fbcfe8;
--color-pink-300: #f9a8d4;
--color-pink-400: #f472b6;
--color-pink-500: #ec4899;
--color-pink-600: #db2777;
--color-pink-700: #be185d;
--color-pink-800: #9d174d;
--color-pink-900: #831843;
--color-pink-950: #500724;
--color-rose-50: #fff1f2;
--color-rose-100: #ffe4e6;
--color-rose-200: #fecdd3;
--color-rose-300: #fda4af;
--color-rose-400: #fb7185;
--color-rose-500: #f43f5e;
--color-rose-600: #e11d48;
--color-rose-700: #be123c;
--color-rose-800: #9f1239;
--color-rose-900: #881337;
--color-rose-950: #4c0519;
/* End: Colors from Tailwind CSS */
--bg-type-u: #006ab3;
--bg-type-s: #1a962b;
--bg-type-a: #f39100;
--bg-type-r: #000000;
--bg-type-u-disabled: #c3cfd7;
--bg-type-s-disabled: #c6d4c8;
--bg-type-a-disabled: #dfd8cf;
--bg-type-r-disabled: #b3b3b3;
--color-type-disabled: var(--color-gray-400);
--color-line-U1: #006ab3;
--color-line-U2: #e2001a;
--color-line-U3: #ffdd00;
--color-line-U4: #0098a1;
--color-line-S1: #1a962b;
--color-line-S2: #b51143;
--color-line-S3: #622181;
--color-line-S4: #be148e;
--color-line-S5: #0089bb;
--color-line-S6: #d3da00;
--bg: var(--color-gray-50); --bg: var(--color-gray-50);
--text: var(--color-gray-950); --text: var(--color-gray-950);
--text-secondary: var(--color-gray-700); --text-secondary: var(--color-gray-700);
--nav-bg: hsl(210, 60%, 50%); --nav-bg: var(--color-indigo-400);
--nav-bg-hover: hsl(210, 60%, 30%); --nav-bg-hover: var(--color-indigo-200);
--nav-bg-visited: hsl(250, 60%, 50%); --nav-bg-visited: var(--color-purple-400);
--nav-text-visited: hsl(250, 60%, 93%); --link: var(--color-indigo-600);
--nav-text: hsl(210, 60%, 93%); --link-hover: var(--color-indigo-800);
--link: hsl(210, 60%, 30%); --link-visited: var(--color-purple-600);
--link-hover: hsl(210, 60%, 10%);
--link-visited: hsl(250, 60%, 30%);
--station-bg: var(--color-gray-50); --station-bg: var(--color-gray-50);
--station-bg-hover: var(--color-gray-400); --station-bg-hover: var(--color-gray-400);
--station-border: var(--color-gray-400); --station-border: var(--color-gray-400);
--station-border-hover: var(--color-gray-600); --station-border-hover: var(--color-gray-600);
--elevator-bg: var(--color-gray-100); --elevator-bg: var(--color-gray-100);
--backdrop: rgb(0 0 0 / 20%);
--elevator-list-gap: 3rem; --elevator-list-gap: 3rem;
--item-radius: 1.5rem; --item-radius: 1.5rem;
--btn-radius: 0.8rem; --btn-radius: 0.8rem;
@ -38,42 +314,97 @@
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
--bg-type-u-disabled: #627f93;
--bg-type-s-disabled: #688b6d;
--bg-type-a-disabled: #aa987f;
--bg-type-r-disabled: #333333;
--color-type-disabled: var(--color-gray-200);
--bg: var(--color-gray-950); --bg: var(--color-gray-950);
--text: var(--color-gray-50); --text: var(--color-gray-50);
--text-secondary: var(--color-gray-400); --text-secondary: var(--color-gray-400);
--nav-bg: hsl(210, 60%, 30%); --nav-bg: var(--color-indigo-800);
--nav-bg-hover: hsl(210, 60%, 10%); --nav-bg-hover: var(--color-indigo-950);
--nav-bg-visited: hsl(250, 60%, 30%); --nav-bg-visited: var(--color-purple-800);
--link: hsl(210, 60%, 60%); --link: var(--color-indigo-400);
--link-hover: hsl(210, 60%, 60%); --link-hover: var(--color-indigo-200);
--link-visited: hsl(250, 60%, 70%); --link-visited: var(--color-purple-400);
--station-bg: var(--color-gray-950); --station-bg: var(--color-gray-950);
--station-bg-hover: var(--color-gray-600); --station-bg-hover: var(--color-gray-600);
--station-border: var(--color-gray-600); --station-border: var(--color-gray-600);
--station-border-hover: var(--color-gray-400); --station-border-hover: var(--color-gray-400);
--elevator-bg: var(--color-gray-900); --elevator-bg: var(--color-gray-900);
--backdrop: rgb(100% 100% 100% / 20%);
} }
} }
.hidden { body.has-dialog {
overflow: hidden;
}
.dialogs {
display: none; display: none;
position: fixed;
overflow-y: auto;
max-width: 100vw;
padding: 1em;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 99;
background: var(--backdrop);
}
.dialogs.active {
display: block;
}
.dialogs [role=dialog] {
box-sizing: border-box;
max-width: 800px;
margin: 0 auto;
padding: var(--space-xl);
padding-bottom: var(--space-2xl);
background-color: var(--bg);
color: var(--text);
overflow: hidden;
position: absolute;
right: 0;
bottom: 0;
left: 0;
border-radius: var(--item-radius) var(--item-radius) 0 0;
}
@media screen and (min-width: 576px) {
.dialogs [role=dialog] {
position: relative;
border-radius: var(--item-radius);
}
}
.dialogs [role=dialog] button.close-modal {
position: absolute;
top: 0;
right: 0;
border-radius: 0 0 0 var(--btn-radius);
border: none;
}
.hidden {
display: none !important;
} }
.text-red { .text-red {
color: #e32d2d; color: var(--color-red-800);
} }
.text-orange { .text-orange {
color: #cc6300; color: var(--color-orange-500);
} }
.text-green { .text-green {
color: #19942e; color: var(--color-green-600);
} }
body { body {
font-family: system-ui, ui-sans-serif, sans-serif; font-family: system-ui, ui-sans-serif, sans-serif;
font-size: 1.2rem; font-size: 1rem;
background-color: var(--bg); background-color: var(--bg);
color: var(--text); color: var(--text);
} }
@ -86,7 +417,7 @@ a {
color: var(--link); color: var(--link);
text-decoration: none; text-decoration: none;
} }
a:hover { a:hover, a:visited:hover {
text-decoration: underline; text-decoration: underline;
color: var(--link-hover); color: var(--link-hover);
} }
@ -104,18 +435,18 @@ nav ul {
} }
nav ul li a { nav ul li a {
font-weight: bold; font-weight: bold;
color: var(--nav-text); color: var(--text);
text-decoration: none; text-decoration: none;
background-color: var(--nav-bg); background-color: var(--nav-bg);
padding: var(--space-m) var(--space-l); padding: var(--space-m) var(--space-l);
cursor: pointer; cursor: pointer;
} }
nav ul li a:hover { nav ul li a:hover, nav ul li a:visited:hover {
text-decoration: underline; text-decoration: underline;
background-color: var(--nav-bg-hover); background-color: var(--nav-bg-hover);
} }
nav ul li a:visited { nav ul li a:visited {
color: var(--nav-text-visited); color: var(--text);
background-color: var(--nav-bg-visited); background-color: var(--nav-bg-visited);
} }
@ -124,6 +455,13 @@ main {
max-width: 960px; max-width: 960px;
} }
footer {
display: flex;
justify-content: center;
margin: 1em auto;
max-width: 960px;
}
button, input { button, input {
background-color: var(--station-bg); background-color: var(--station-bg);
color: var(--text); color: var(--text);
@ -142,13 +480,124 @@ button:hover {
background-color: var(--station-bg-hover); background-color: var(--station-bg-hover);
border-color: var(--station-border-hover); border-color: var(--station-border-hover);
} }
button[aria-pressed=true] {
div.filters { background-color: var(--nav-bg-visited);
display: flex;
gap: var(--space-m);
justify-items: stretch;
} }
div.filters button, div.filters input { button.size-s {
font-size: 0.8rem;
padding: var(--space-s) var(--space-m);
}
button.size-m {
padding: var(--space-m) var(--space-l);
}
button.size-l {
font-size: 1.5rem;
padding: var(--space-m) var(--space-l);
}
div#updateInfo {
display: flex;
align-items: center;
gap: var(--space-m);
justify-content: end;
flex-direction: column;
margin-bottom: var(--space-l);
}
@media screen and (min-width: 576px) {
div#updateInfo {
flex-direction: row;
}
}
div#updateInfo #oldDataWarning {
color: var(--color-red-600);
font-weight: bold;
}
div#filters {
display: flex;
flex-wrap: wrap;
gap: var(--space-m);
justify-content: center;
}
div#filters > * {
width: 100%;
justify-content: center;
text-align: center;
}
@media screen and (min-width: 576px) {
div#filters > * {
width: auto;
min-width: 0;
}
}
div#filters input#searchStation {
flex-grow: 1;
font-size: 1.5rem;
min-width: 10ch;
}
div#filters button#stationsNearMe {
font-size: 1.5rem;
}
div#filters div#typeFilter {
font-size: 1.5rem;
display: flex;
justify-content: center;
flex-shrink: 0;
min-height: var(--space-2xl);
gap: var(--space-m);
}
div#filters div#typeFilter button.typeChip {
flex-shrink: 0;
padding: 0;
height: var(--space-2xl);
aspect-ratio: 1/1;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
border-radius: 9999px;
}
div#filters div#typeFilter button.typeChip[data-type=U], div#filters div#typeFilter button.typeChip[data-type=S], div#filters div#typeFilter button.typeChip[data-type=A], div#filters div#typeFilter button.typeChip[data-type=R] {
border: solid 2px var(--text);
}
div#filters div#typeFilter button.typeChip[data-type=U][data-pressed=false], div#filters div#typeFilter button.typeChip[data-type=S][data-pressed=false], div#filters div#typeFilter button.typeChip[data-type=A][data-pressed=false], div#filters div#typeFilter button.typeChip[data-type=R][data-pressed=false] {
background-color: var(--color-gray-500);
color: var(--color-type-disabled);
}
div#filters div#typeFilter button.typeChip[data-type=U][data-pressed=false][data-type=U], div#filters div#typeFilter button.typeChip[data-type=S][data-pressed=false][data-type=U], div#filters div#typeFilter button.typeChip[data-type=A][data-pressed=false][data-type=U], div#filters div#typeFilter button.typeChip[data-type=R][data-pressed=false][data-type=U] {
background-color: var(--bg-type-u-disabled);
}
div#filters div#typeFilter button.typeChip[data-type=U][data-pressed=false][data-type=S], div#filters div#typeFilter button.typeChip[data-type=S][data-pressed=false][data-type=S], div#filters div#typeFilter button.typeChip[data-type=A][data-pressed=false][data-type=S], div#filters div#typeFilter button.typeChip[data-type=R][data-pressed=false][data-type=S] {
background-color: var(--bg-type-s-disabled);
}
div#filters div#typeFilter button.typeChip[data-type=U][data-pressed=false][data-type=A], div#filters div#typeFilter button.typeChip[data-type=S][data-pressed=false][data-type=A], div#filters div#typeFilter button.typeChip[data-type=A][data-pressed=false][data-type=A], div#filters div#typeFilter button.typeChip[data-type=R][data-pressed=false][data-type=A] {
background-color: var(--bg-type-a-disabled);
}
div#filters div#typeFilter button.typeChip[data-type=U][data-pressed=false][data-type=R], div#filters div#typeFilter button.typeChip[data-type=S][data-pressed=false][data-type=R], div#filters div#typeFilter button.typeChip[data-type=A][data-pressed=false][data-type=R], div#filters div#typeFilter button.typeChip[data-type=R][data-pressed=false][data-type=R] {
background-color: var(--bg-type-r-disabled);
}
div#filters div#typeFilter button.typeChip[data-type=U][data-pressed=false]::after, div#filters div#typeFilter button.typeChip[data-type=S][data-pressed=false]::after, div#filters div#typeFilter button.typeChip[data-type=A][data-pressed=false]::after, div#filters div#typeFilter button.typeChip[data-type=R][data-pressed=false]::after {
position: absolute;
content: "";
width: 144%;
height: 0.4rem;
background-color: var(--color-gray-700);
transform: rotate(-45deg);
}
button#initialLoad {
font-size: 1.5rem;
}
button.loadOSM {
width: fit-content;
}
div#errorMessage {
border: solid 2px var(--color-red-600);
border-radius: var(--item-radius);
padding: var(--space-xl) var(--space-xl);
margin-top: var(--space-l);
font-size: 1.5rem; font-size: 1.5rem;
} }
@ -159,6 +608,11 @@ ul#stationList {
flex-direction: column; flex-direction: column;
gap: var(--space-m); gap: var(--space-m);
} }
ul#stationList li#filterInfo {
text-align: center;
padding: var(--space-l) var(--space-xl);
font-style: italic;
}
ul#stationList > li.station { ul#stationList > li.station {
background-color: var(--station-bg); background-color: var(--station-bg);
border-radius: var(--item-radius); border-radius: var(--item-radius);
@ -171,9 +625,20 @@ ul#stationList > li.station > div.stationSummary {
display: flex; display: flex;
gap: var(--space-m); gap: var(--space-m);
} }
ul#stationList > li.station > div.stationSummary div.typeList { ul#stationList > li.station > div.stationSummary div.symbol {
position: relative;
}
ul#stationList > li.station > div.stationSummary div.symbol div.typeList {
flex-shrink: 0; flex-shrink: 0;
} }
ul#stationList > li.station > div.stationSummary div.symbol div.distance {
position: absolute;
width: 100%;
bottom: -2lh;
text-align: center;
color: var(--text-secondary);
font-size: 0.9em;
}
ul#stationList > li.station > div.stationSummary div.stationTitle { ul#stationList > li.station > div.stationSummary div.stationTitle {
align-self: stretch; align-self: stretch;
flex-grow: 1; flex-grow: 1;
@ -208,17 +673,23 @@ ul#stationList > li.station > div.stationSummary div.stationTitle > div.elevator
} }
ul#stationList > li.station > details { ul#stationList > li.station > details {
width: 100%; width: 100%;
display: flex;
flex-direction: column;
} }
ul#stationList > li.station > details summary { ul#stationList > li.station > details summary {
width: 100%;
list-style: none; list-style: none;
display: inline-flex;
cursor: pointer;
margin-left: calc(var(--space-m) + var(--type-icon-size));
}
ul#stationList > li.station > details summary > span {
border: solid 2px var(--station-border); border: solid 2px var(--station-border);
display: inline-flex; display: inline-flex;
padding: var(--space-m) var(--space-l); padding: var(--space-m) var(--space-l);
border-radius: var(--btn-radius); border-radius: var(--btn-radius);
cursor: pointer;
margin-left: calc(var(--space-m) + var(--type-icon-size));
} }
ul#stationList > li.station > details summary:hover { ul#stationList > li.station > details summary:hover > span {
background-color: var(--station-bg-hover); background-color: var(--station-bg-hover);
border-color: var(--station-border-hover); border-color: var(--station-border-hover);
} }
@ -241,6 +712,20 @@ ul#stationList > li.station > details summary::after {
ul#stationList > li.station > details[open] summary { ul#stationList > li.station > details[open] summary {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
ul#stationList > li.station > details > div.comment {
position: relative;
z-index: 20;
display: inline-flex;
margin-left: calc(var(--space-m) + var(--type-icon-size));
padding-bottom: var(--space-m);
border-bottom: solid 2px var(--station-border);
}
ul#stationList > li.station > details > div.comment > p {
display: block;
border: solid 2px var(--nav-bg);
border-radius: var(--btn-radius);
padding: var(--space-l);
}
ul#stationList > li.station > details > ul { ul#stationList > li.station > details > ul {
padding: 0; padding: 0;
list-style: none; list-style: none;
@ -255,19 +740,29 @@ ul#stationList > li.station > details > ul > li.elevator {
display: flex; display: flex;
align-items: start; align-items: start;
flex-wrap: nowrap; flex-wrap: nowrap;
padding: var(--space-m);
gap: var(--space-m); gap: var(--space-m);
position: relative; position: relative;
margin: 0 1rem 1rem; margin: 0 var(--space-s) var(--space-s);
padding-right: var(--space-2xl); padding: var(--space-s);
} }
@media screen and (min-width: 576px) { @media screen and (min-width: 576px) {
ul#stationList > li.station > details > ul > li.elevator { ul#stationList > li.station > details > ul > li.elevator {
margin: 0 var(--space-2xl) 1rem; margin: 0 var(--space-2xl) 1rem;
padding: var(--space-m);
padding-right: var(--space-2xl);
} }
} }
ul#stationList > li.station > details > ul > li.elevator > span { ul#stationList > li.station > details > ul > li.elevator > .stateIcon {
flex-shrink: 0; flex-shrink: 0;
display: none;
}
@media screen and (min-width: 576px) {
ul#stationList > li.station > details > ul > li.elevator > .stateIcon {
display: inline;
}
}
ul#stationList > li.station > details > ul > li.elevator:first-child {
padding-top: var(--space-l);
} }
ul#stationList > li.station > details > ul > li.elevator:not(:first-child):before { ul#stationList > li.station > details > ul > li.elevator:not(:first-child):before {
content: ""; content: "";
@ -295,11 +790,25 @@ ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .ele
font-weight: bold; font-weight: bold;
grid-column: 1/-1; grid-column: 1/-1;
} }
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .lineList { ul#stationList > li.station > details > ul > li.elevator > div.elevatorData div.firstRow {
grid-column: 1/-1;
display: flex;
gap: var(--space-m);
}
@media screen and (min-width: 576px) {
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData div.firstRow.hiddenOnDesktop {
display: none;
}
}
@media screen and (min-width: 576px) {
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData div.firstRow .stateIcon {
display: none;
}
}
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData div.firstRow .lineList {
display: flex; display: flex;
gap: var(--space-m); gap: var(--space-m);
align-items: center; align-items: center;
grid-column: 1/-1;
min-height: var(--space-2xl); min-height: var(--space-2xl);
} }
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData hr { ul#stationList > li.station > details > ul > li.elevator > div.elevatorData hr {
@ -316,8 +825,15 @@ ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .osm
border-radius: var(--item-radius); border-radius: var(--item-radius);
padding: var(--space-l); padding: var(--space-l);
} }
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .osm h4 { ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .osm .osmHeading {
margin-top: 0; display: flex;
align-items: center;
flex-wrap: wrap;
gap: var(--space-l);
margin-bottom: var(--space-l);
}
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .osm .osmHeading h4 {
margin: 0;
} }
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .osm dl div dt { ul#stationList > li.station > details > ul > li.elevator > div.elevatorData .osm dl div dt {
min-width: 18ch; min-width: 18ch;
@ -377,7 +893,7 @@ ul#stationList > li.station > details > ul > li.elevator > div.elevatorData dl d
font-weight: bold; font-weight: bold;
min-width: 15ch; min-width: 15ch;
} }
@media (max-width: 576px) { @media (max-width: 400px) {
ul#stationList > li.station > details > ul > li.elevator > div.elevatorData dl div dt { ul#stationList > li.station > details > ul > li.elevator > div.elevatorData dl div dt {
width: 100%; width: 100%;
} }
@ -393,63 +909,66 @@ ul#stationList > li.station > details > ul > li.elevator > div.elevatorData dl d
} }
} }
span.lineChip, span.typeChip { span.lineChip, span.typeChip, button.typeChip {
font-weight: bold; font-weight: bold;
font-size: 0.8em; font-size: 0.8em;
text-align: center; text-align: center;
box-sizing: border-box; box-sizing: border-box;
} }
span.lineChip[data-type=U], span.typeChip[data-type=U] { span.lineChip[data-type=U], span.typeChip[data-type=U], button.typeChip[data-type=U] {
background-color: #006ab3; background-color: var(--bg-type-u);
color: #ffffff; color: var(--color-white);
} }
span.lineChip[data-type=U][data-line=U1], span.typeChip[data-type=U][data-line=U1] { span.lineChip[data-type=U][data-line=U1], span.typeChip[data-type=U][data-line=U1], button.typeChip[data-type=U][data-line=U1] {
background-color: #006ab3; background-color: var(--color-line-U1);
} }
span.lineChip[data-type=U][data-line=U2], span.typeChip[data-type=U][data-line=U2] { span.lineChip[data-type=U][data-line=U2], span.typeChip[data-type=U][data-line=U2], button.typeChip[data-type=U][data-line=U2] {
background-color: #e2001a; background-color: var(--color-line-U2);
} }
span.lineChip[data-type=U][data-line=U3], span.typeChip[data-type=U][data-line=U3] { span.lineChip[data-type=U][data-line=U3], span.typeChip[data-type=U][data-line=U3], button.typeChip[data-type=U][data-line=U3] {
background-color: #ffdd00; background-color: var(--color-line-U3);
color: #000000; color: var(--color-black);
} }
span.lineChip[data-type=U][data-line=U4], span.typeChip[data-type=U][data-line=U4] { span.lineChip[data-type=U][data-line=U4], span.typeChip[data-type=U][data-line=U4], button.typeChip[data-type=U][data-line=U4] {
background-color: #0098a1; background-color: var(--color-line-U4);
} }
span.lineChip[data-type=S], span.typeChip[data-type=S] { span.lineChip[data-type=U][data-line=U5], span.typeChip[data-type=U][data-line=U5], button.typeChip[data-type=U][data-line=U5] {
background-color: #1a962b; background-color: var(--color-line-U5);
color: #ffffff;
} }
span.lineChip[data-type=S][data-line=S1], span.typeChip[data-type=S][data-line=S1] { span.lineChip[data-type=S], span.typeChip[data-type=S], button.typeChip[data-type=S] {
background-color: #1a962b; background-color: var(--bg-type-s);
color: var(--color-white);
} }
span.lineChip[data-type=S][data-line=S2], span.typeChip[data-type=S][data-line=S2] { span.lineChip[data-type=S][data-line=S1], span.typeChip[data-type=S][data-line=S1], button.typeChip[data-type=S][data-line=S1] {
background-color: #b51143; background-color: var(--color-line-S1);
} }
span.lineChip[data-type=S][data-line=S3], span.typeChip[data-type=S][data-line=S3] { span.lineChip[data-type=S][data-line=S2], span.typeChip[data-type=S][data-line=S2], button.typeChip[data-type=S][data-line=S2] {
background-color: #622181; background-color: var(--color-line-S2);
} }
span.lineChip[data-type=S][data-line=S4], span.typeChip[data-type=S][data-line=S4] { span.lineChip[data-type=S][data-line=S3], span.typeChip[data-type=S][data-line=S3], button.typeChip[data-type=S][data-line=S3] {
background-color: #be148e; background-color: var(--color-line-S3);
} }
span.lineChip[data-type=S][data-line=S5], span.typeChip[data-type=S][data-line=S5] { span.lineChip[data-type=S][data-line=S4], span.typeChip[data-type=S][data-line=S4], button.typeChip[data-type=S][data-line=S4] {
background-color: #0089bb; background-color: var(--color-line-S4);
} }
span.lineChip[data-type=S][data-line=S6], span.typeChip[data-type=S][data-line=S6] { span.lineChip[data-type=S][data-line=S5], span.typeChip[data-type=S][data-line=S5], button.typeChip[data-type=S][data-line=S5] {
background-color: #d3da00; background-color: var(--color-line-S5);
color: #000000;
} }
span.lineChip[data-type=A], span.typeChip[data-type=A] { span.lineChip[data-type=S][data-line=S6], span.typeChip[data-type=S][data-line=S6], button.typeChip[data-type=S][data-line=S6] {
background-color: #f39100; background-color: var(--color-line-S6);
color: #ffffff; color: var(--color-black);
} }
span.lineChip[data-type=R], span.typeChip[data-type=R] { span.lineChip[data-type=A], span.typeChip[data-type=A], button.typeChip[data-type=A] {
background-color: #000000; background-color: var(--bg-type-a);
color: #ffffff; color: var(--color-white);
}
span.lineChip[data-type=R], span.typeChip[data-type=R], button.typeChip[data-type=R] {
background-color: var(--bg-type-r);
color: var(--color-white);
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
span.lineChip[data-type=R], span.typeChip[data-type=R] { span.lineChip[data-type=R], span.typeChip[data-type=R], button.typeChip[data-type=R] {
border: solid 2px #ffffff; border: solid 2px var(--color-white);
} }
} }
@ -559,5 +1078,27 @@ span[data-icon][data-icon=up-down] {
span[data-icon][data-icon=location-searching] { span[data-icon][data-icon=location-searching] {
mask-image: url(/md_icons/location-searching.svg); mask-image: url(/md_icons/location-searching.svg);
} }
span[data-icon][data-icon=load] {
mask-image: url(/md_icons/load.svg);
}
span[data-icon][data-icon=close] {
mask-image: url(/md_icons/close.svg);
}
span[data-icon].spinner {
animation-delay: 0s;
animation-direction: normal;
animation-duration: 1s;
animation-fill-mode: none;
animation-iteration-count: infinite;
animation-name: spinner;
animation-play-state: running;
animation-timing-function: linear;
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}
/*# sourceMappingURL=style.css.map */ /*# sourceMappingURL=style.css.map */

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["style.scss"],"names":[],"mappings":"AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAmBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAzBA;EA/BF;IAgCI;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;;AAcJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EAGA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGE;EACE;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAOV;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAIF;EACE;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;EAEA;;AAEA;EAPF;IAQI;;;AAIJ;EACE;EAEA;EACA;EACA;EACA;;AAEA;EARF;IASI;IACA;IACA;;;AAMR;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EA1BF;IA2BI;;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAEA;EAKA;;AAJA;EATF;IAUI;;;AAMF;EACE;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EAEA;;AACA;EARF;IASI;;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKE;EACE;;AAMR;EACE;EACA;EACA;EAEA;EACA;;AAEA;EACE;EAEA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAIA;EACE;;AAKF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EARF;IASI;;;AAIJ;EACE;EACA;EAEA;;AAEA;EANF;IAOI;;;;AAYpB;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EAJF;IAKI;;;;AAKN;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EAEE;EAMA;EACA;EACA;EACA;EACA;EACA;EACA;;AAVA;EACE;;AAWF;EAIE;EACA;;;AAMJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE","file":"style.css"} {"version":3,"sourceRoot":"","sources":["style.scss","colors.scss"],"names":[],"mappings":"AAMA;ACLE;;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EAwBA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AACA;ED/PA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAIA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EAgCA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAtCA;EApDF;IAsDI;IACA;IACA;IACA;IACA;IAGA;IACA;IACA;IAEA;IACA;IACA;IAEA;IACA;IACA;IAEA;IACA;IACA;IACA;IACA;IAEA;;;;AAeF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAfF;IAgBI;IACA;;;AAIA;EACE;EACA;EACA;EACA;EACA;;;AAMR;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EAGA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGE;EACE;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAOV;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EARF;IASI;;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AACA;EAJF;IAKI;IACA;;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAQZ;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;;AAKJ;EACE;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;EAEA;;AAEA;EAPF;IAQI;;;AAIJ;EACE;EAEA;EACA;EACA;EACA;;AAEA;EARF;IASI;IACA;IACA;;;AAMR;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAjCF;IAkCI;;;AAIJ;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EAEA;EACA;;AACA;EATF;IAUI;IACA;IACA;;;AAIF;EACE;EACA;;AACA;EAHF;IAII;;;AAKJ;EACE;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EAEA;;AACA;EARF;IASI;;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGE;EADF;IAEI;;;AAKF;EADF;IAEI;;;AAKJ;EACE;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAOA;EACE;;AAMR;EACE;EACA;EACA;EAEA;EACA;;AAEA;EACE;EAEA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAIA;EACE;;AAKF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EARF;IASI;;;AAIJ;EACE;EACA;EAEA;;AAEA;EANF;IAOI;;;;AAYpB;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EAJF;IAKI;;;;AAMN;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EAEE;EAMA;EACA;EACA;EACA;EACA;EACA;EACA;;AAVA;EACE;;AAWF;EAIE;EACA;;;AAMJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAIJ;EACE;IACE","file":"style.css"}

View file

@ -1,52 +1,89 @@
@import "colors";
$mobileBreakpoint: 576px; $mobileBreakpoint: 576px;
$smallBreakpoint: 400px;
$mediumBreakpoint: 800px; $mediumBreakpoint: 800px;
:root { :root {
--color-gray-50: hsl(60, 9%, 98%); @include tailwindColors();
--color-gray-100: hsl(60, 5%, 96%);
--color-gray-200: hsl(20, 6%, 90%); // LineType Colors
--color-gray-300: hsl(24, 6%, 83%); $colorTypeU: #006ab3;
--color-gray-400: hsl(24, 5%, 64%); $colorTypeS: #1a962b;
--color-gray-500: hsl(25, 5%, 45%); $colorTypeA: #f39100;
--color-gray-600: hsl(33, 5%, 32%); $colorTypeR: #000000;
--color-gray-700: hsl(30, 6%, 25%); --bg-type-u: #{$colorTypeU};
--color-gray-800: hsl(12, 6%, 15%); --bg-type-s: #{$colorTypeS};
--color-gray-900: hsl(24, 10%, 10%); --bg-type-a: #{$colorTypeA};
--color-gray-950: hsl(20, 14%, 4%); --bg-type-r: #{$colorTypeR};
--bg-type-u-disabled: #{scale-color($colorTypeU, $saturation: -80%, $lightness: +70%)};
--bg-type-s-disabled: #{scale-color($colorTypeS, $saturation: -80%, $lightness: +70%)};
--bg-type-a-disabled: #{scale-color($colorTypeA, $saturation: -80%, $lightness: +70%)};
--bg-type-r-disabled: #{scale-color($colorTypeR, $saturation: -80%, $lightness: +70%)};
--color-type-disabled: var(--color-gray-400);
// Line Colors
--color-line-U1: #006ab3;
--color-line-U2: #e2001a;
--color-line-U3: #ffdd00;
--color-line-U4: #0098a1;
--color-line-S1: #1a962b;
--color-line-S2: #b51143;
--color-line-S3: #622181;
--color-line-S4: #be148e;
--color-line-S5: #0089bb;
--color-line-S6: #d3da00;
// Component Styles
--bg: var(--color-gray-50); --bg: var(--color-gray-50);
--text: var(--color-gray-950); --text: var(--color-gray-950);
--text-secondary: var(--color-gray-700); --text-secondary: var(--color-gray-700);
--nav-bg: hsl(210, 60%, 50%);
--nav-bg-hover: hsl(210, 60%, 30%); --nav-bg: var(--color-indigo-400);
--nav-bg-visited: hsl(250, 60%, 50%); --nav-bg-hover: var(--color-indigo-200);
--nav-text-visited: hsl(250, 60%, 93%); --nav-bg-visited: var(--color-purple-400);
--nav-text: hsl(210, 60%, 93%);
--link: hsl(210, 60%, 30%); --link: var(--color-indigo-600);
--link-hover: hsl(210, 60%, 10%); --link-hover: var(--color-indigo-800);
--link-visited: hsl(250, 60%, 30%); --link-visited: var(--color-purple-600);
--station-bg: var(--color-gray-50); --station-bg: var(--color-gray-50);
--station-bg-hover: var(--color-gray-400); --station-bg-hover: var(--color-gray-400);
--station-border: var(--color-gray-400); --station-border: var(--color-gray-400);
--station-border-hover: var(--color-gray-600); --station-border-hover: var(--color-gray-600);
--elevator-bg: var(--color-gray-100); --elevator-bg: var(--color-gray-100);
--backdrop: rgb(0 0 0 / 20%);
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
// LineType Colors
--bg-type-u-disabled: #{scale-color($colorTypeU, $saturation: -80%, $lightness: +20%)};
--bg-type-s-disabled: #{scale-color($colorTypeS, $saturation: -80%, $lightness: +20%)};
--bg-type-a-disabled: #{scale-color($colorTypeA, $saturation: -80%, $lightness: +20%)};
--bg-type-r-disabled: #{scale-color($colorTypeR, $saturation: -80%, $lightness: +20%)};
--color-type-disabled: var(--color-gray-200);
// Component Styles
--bg: var(--color-gray-950); --bg: var(--color-gray-950);
--text: var(--color-gray-50); --text: var(--color-gray-50);
--text-secondary: var(--color-gray-400); --text-secondary: var(--color-gray-400);
--nav-bg: hsl(210, 60%, 30%);
--nav-bg-hover: hsl(210, 60%, 10%); --nav-bg: var(--color-indigo-800);
--nav-bg-visited: hsl(250, 60%, 30%); --nav-bg-hover: var(--color-indigo-950);
--link: hsl(210, 60%, 60%); --nav-bg-visited: var(--color-purple-800);
--link-hover: hsl(210, 60%, 60%);
--link-visited: hsl(250, 60%, 70%); --link: var(--color-indigo-400);
--link-hover: var(--color-indigo-200);
--link-visited: var(--color-purple-400);
--station-bg: var(--color-gray-950); --station-bg: var(--color-gray-950);
--station-bg-hover: var(--color-gray-600); --station-bg-hover: var(--color-gray-600);
--station-border: var(--color-gray-600); --station-border: var(--color-gray-600);
--station-border-hover: var(--color-gray-400); --station-border-hover: var(--color-gray-400);
--elevator-bg: var(--color-gray-900); --elevator-bg: var(--color-gray-900);
--backdrop: rgb(100% 100% 100% / 20%);
} }
--elevator-list-gap: 3rem; --elevator-list-gap: 3rem;
@ -60,25 +97,81 @@ $mediumBreakpoint: 800px;
--space-2xl: 3rem; --space-2xl: 3rem;
} }
.hidden { body {
&.has-dialog {
overflow: hidden;
}
}
.dialogs {
display: none; display: none;
position: fixed;
overflow-y: auto;
max-width: 100vw;
padding: 1em;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 99;
background: var(--backdrop);
&.active {
display: block;
}
[role="dialog"] {
box-sizing: border-box;
max-width: 800px;
margin: 0 auto;
padding: var(--space-xl);
padding-bottom: var(--space-2xl);
background-color: var(--bg);
color: var(--text);
overflow: hidden;
position: absolute;
right: 0;
bottom: 0;
left: 0;
border-radius: var(--item-radius) var(--item-radius) 0 0;
@media screen and (min-width: $mobileBreakpoint) {
position: relative;
border-radius: var(--item-radius);
}
button {
&.close-modal {
position: absolute;
top: 0;
right: 0;
border-radius: 0 0 0 var(--btn-radius);
border: none;
}
}
}
}
.hidden {
display: none !important;
} }
.text-red { .text-red {
color: #e32d2d; color: var(--color-red-800);
} }
.text-orange { .text-orange {
color: #cc6300; color: var(--color-orange-500);
} }
.text-green { .text-green {
color: #19942e; color: var(--color-green-600);
} }
body { body {
font-family: system-ui, ui-sans-serif, sans-serif; font-family: system-ui, ui-sans-serif, sans-serif;
font-size: 1.2rem; font-size: 1rem;
// colors // colors
background-color: var(--bg); background-color: var(--bg);
@ -93,7 +186,7 @@ a {
color: var(--link); color: var(--link);
text-decoration: none; text-decoration: none;
&:hover { &:hover, &:visited:hover {
text-decoration: underline; text-decoration: underline;
color: var(--link-hover); color: var(--link-hover);
} }
@ -115,20 +208,20 @@ nav {
li { li {
a { a {
font-weight: bold; font-weight: bold;
color: var(--nav-text); color: var(--text);
text-decoration: none; text-decoration: none;
background-color: var(--nav-bg); background-color: var(--nav-bg);
padding: var(--space-m) var(--space-l); padding: var(--space-m) var(--space-l);
cursor: pointer; cursor: pointer;
&:hover { &:hover, &:visited:hover {
text-decoration: underline; text-decoration: underline;
background-color: var(--nav-bg-hover); background-color: var(--nav-bg-hover);
} }
&:visited { &:visited {
color: var(--nav-text-visited); color: var(--text);
background-color: var(--nav-bg-visited); background-color: var(--nav-bg-visited);
} }
} }
@ -141,6 +234,13 @@ main {
max-width: 960px; max-width: 960px;
} }
footer {
display: flex;
justify-content: center;
margin: 1em auto;
max-width: 960px;
}
button, input { button, input {
background-color: var(--station-bg); background-color: var(--station-bg);
color: var(--text); color: var(--text);
@ -159,16 +259,140 @@ button {
background-color: var(--station-bg-hover); background-color: var(--station-bg-hover);
border-color: var(--station-border-hover); border-color: var(--station-border-hover);
} }
&[aria-pressed='true'] {
background-color: var(--nav-bg-visited);
}
&.size-s {
font-size: 0.8rem;
padding: var(--space-s) var(--space-m);
}
&.size-m {
padding: var(--space-m) var(--space-l);
}
&.size-l {
font-size: 1.5rem;
padding: var(--space-m) var(--space-l);
}
} }
div.filters { div#updateInfo {
display: flex; display: flex;
align-items: center;
gap: var(--space-m); gap: var(--space-m);
justify-items: stretch; justify-content: end;
flex-direction: column;
margin-bottom: var(--space-l);
button, input { @media screen and (min-width: $mobileBreakpoint) {
flex-direction: row;
}
#oldDataWarning {
color: var(--color-red-600);
font-weight: bold;
}
}
div#filters {
display: flex;
flex-wrap: wrap;
gap: var(--space-m);
justify-content: center;
> * {
width: 100%;
justify-content: center;
text-align: center;
@media screen and (min-width: $mobileBreakpoint) {
width: auto;
min-width: 0;
}
}
input#searchStation {
flex-grow: 1;
font-size: 1.5rem;
min-width: 10ch;
}
button#stationsNearMe {
font-size: 1.5rem; font-size: 1.5rem;
} }
div#typeFilter {
font-size: 1.5rem;
display: flex;
justify-content: center;
flex-shrink: 0;
min-height: var(--space-2xl);
gap: var(--space-m);
button.typeChip {
flex-shrink: 0;
padding: 0;
height: var(--space-2xl);
aspect-ratio: 1/1;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
border-radius: 9999px;
&[data-type="U"], &[data-type="S"], &[data-type="A"], &[data-type="R"] {
border: solid 2px var(--text);
&[data-pressed="false"] {
background-color: var(--color-gray-500);
color: var(--color-type-disabled);
&[data-type="U"] {
background-color: var(--bg-type-u-disabled);
}
&[data-type="S"] {
background-color: var(--bg-type-s-disabled);
}
&[data-type="A"] {
background-color: var(--bg-type-a-disabled);
}
&[data-type="R"] {
background-color: var(--bg-type-r-disabled);
}
&::after {
position: absolute;
content: '';
width: 144%;
height: 0.4rem;
background-color: var(--color-gray-700);
transform: rotate(-45deg);
}
}
}
}
}
}
button#initialLoad {
font-size: 1.5rem;
}
button.loadOSM {
width: fit-content;
}
div#errorMessage {
border: solid 2px var(--color-red-600);
border-radius: var(--item-radius);
padding: var(--space-xl) var(--space-xl);
margin-top: var(--space-l);
font-size: 1.5rem;
} }
ul#stationList { ul#stationList {
@ -178,6 +402,12 @@ ul#stationList {
flex-direction: column; flex-direction: column;
gap: var(--space-m); gap: var(--space-m);
li#filterInfo {
text-align: center;
padding: var(--space-l) var(--space-xl);
font-style: italic;
}
> li.station { > li.station {
background-color: var(--station-bg); background-color: var(--station-bg);
border-radius: var(--item-radius); border-radius: var(--item-radius);
@ -190,10 +420,24 @@ ul#stationList {
display: flex; display: flex;
gap: var(--space-m); gap: var(--space-m);
div.symbol {
position: relative;
div.typeList { div.typeList {
flex-shrink: 0; flex-shrink: 0;
} }
div.distance {
position: absolute;
width: 100%;
bottom: -2lh;
text-align: center;
color: var(--text-secondary);
font-size: 0.9em;
}
}
div.stationTitle { div.stationTitle {
align-self: stretch; align-self: stretch;
@ -234,20 +478,29 @@ ul#stationList {
> details { > details {
width: 100%; width: 100%;
display: flex;
flex-direction: column;
summary { summary {
width: 100%;
list-style: none; list-style: none;
display: inline-flex;
cursor: pointer;
margin-left: calc(var(--space-m) + var(--type-icon-size));
> span {
border: solid 2px var(--station-border); border: solid 2px var(--station-border);
display: inline-flex; display: inline-flex;
padding: var(--space-m) var(--space-l); padding: var(--space-m) var(--space-l);
border-radius: var(--btn-radius); border-radius: var(--btn-radius);
cursor: pointer; }
margin-left: calc(var(--space-m) + var(--type-icon-size));
&:hover { &:hover {
> span {
background-color: var(--station-bg-hover); background-color: var(--station-bg-hover);
border-color: var(--station-border-hover); border-color: var(--station-border-hover);
} }
}
&::after { &::after {
position: absolute; position: absolute;
@ -270,6 +523,22 @@ ul#stationList {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
> div.comment {
position: relative;
z-index: 20;
display: inline-flex;
margin-left: calc(var(--space-m) + var(--type-icon-size));
padding-bottom: var(--space-m);
border-bottom: solid 2px var(--station-border);
> p {
display: block;
border: solid 2px var(--nav-bg);
border-radius: var(--btn-radius);
padding: var(--space-l);
}
}
> ul { > ul {
padding: 0; padding: 0;
list-style: none; list-style: none;
@ -284,20 +553,29 @@ ul#stationList {
display: flex; display: flex;
align-items: start; align-items: start;
flex-wrap: nowrap; flex-wrap: nowrap;
padding: var(--space-m);
gap: var(--space-m); gap: var(--space-m);
position: relative; position: relative;
margin: 0 1rem 1rem; margin: 0 var(--space-s) var(--space-s);
padding: var(--space-s);
@media screen and (min-width: $mobileBreakpoint) { @media screen and (min-width: $mobileBreakpoint) {
margin: 0 var(--space-2xl) 1rem; margin: 0 var(--space-2xl) 1rem;
padding: var(--space-m);
padding-right: var(--space-2xl);
} }
padding-right: var(--space-2xl);
// elevator icon // elevator icon
> span { > .stateIcon {
flex-shrink: 0; flex-shrink: 0;
display: none;
@media screen and (min-width: $mobileBreakpoint) {
display: inline;
}
}
// space above first elevator
&:first-child {
padding-top: var(--space-l);
} }
// divider between elevators // divider between elevators
@ -328,13 +606,32 @@ ul#stationList {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
div.firstRow {
grid-column: 1 / -1;
display: flex;
gap: var(--space-m);
&.hiddenOnDesktop {
@media screen and (min-width: $mobileBreakpoint) {
display: none;
}
}
.stateIcon {
@media screen and (min-width: $mobileBreakpoint) {
display: none;
}
}
.lineList { .lineList {
display: flex; display: flex;
gap: var(--space-m); gap: var(--space-m);
align-items: center; align-items: center;
grid-column: 1 / -1;
min-height: var(--space-2xl); min-height: var(--space-2xl);
} }
}
hr { hr {
grid-column: 1 / -1; grid-column: 1 / -1;
@ -351,9 +648,18 @@ ul#stationList {
border-radius: var(--item-radius); border-radius: var(--item-radius);
padding: var(--space-l); padding: var(--space-l);
.osmHeading {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: var(--space-l);
margin-bottom: var(--space-l);
h4 { h4 {
margin-top: 0; margin: 0;
} }
}
dl { dl {
div { div {
@ -431,7 +737,7 @@ ul#stationList {
font-weight: bold; font-weight: bold;
min-width: 15ch; min-width: 15ch;
@media (max-width: $mobileBreakpoint) { @media (max-width: $smallBreakpoint) {
width: 100%; width: 100%;
} }
} }
@ -455,79 +761,84 @@ ul#stationList {
} }
} }
span.lineChip, span.typeChip { span.lineChip, span.typeChip, button.typeChip {
font-weight: bold; font-weight: bold;
font-size: 0.8em; font-size: 0.8em;
text-align: center; text-align: center;
box-sizing: border-box; box-sizing: border-box;
&[data-type="U"] { &[data-type="U"] {
background-color: #006ab3; background-color: var(--bg-type-u);
color: #ffffff; color: var(--color-white);
&[data-line="U1"] { &[data-line="U1"] {
background-color: #006ab3; background-color: var(--color-line-U1);
} }
&[data-line="U2"] { &[data-line="U2"] {
background-color: #e2001a; background-color: var(--color-line-U2);
} }
&[data-line="U3"] { &[data-line="U3"] {
background-color: #ffdd00; background-color: var(--color-line-U3);
color: #000000; color: var(--color-black);
} }
&[data-line="U4"] { &[data-line="U4"] {
background-color: #0098a1; background-color: var(--color-line-U4);
}
&[data-line="U5"] {
background-color: var(--color-line-U5);
} }
} }
&[data-type="S"] { &[data-type="S"] {
background-color: #1a962b; background-color: var(--bg-type-s);
color: #ffffff; color: var(--color-white);
&[data-line="S1"] { &[data-line="S1"] {
background-color: #1a962b; background-color: var(--color-line-S1);
} }
&[data-line="S2"] { &[data-line="S2"] {
background-color: #b51143; background-color: var(--color-line-S2);
} }
&[data-line="S3"] { &[data-line="S3"] {
background-color: #622181; background-color: var(--color-line-S3);
} }
&[data-line="S4"] { &[data-line="S4"] {
background-color: #be148e; background-color: var(--color-line-S4);
} }
&[data-line="S5"] { &[data-line="S5"] {
background-color: #0089bb; background-color: var(--color-line-S5);
} }
&[data-line="S6"] { &[data-line="S6"] {
background-color: #d3da00; background-color: var(--color-line-S6);
color: #000000; color: var(--color-black);
} }
} }
&[data-type="A"] { &[data-type="A"] {
background-color: #f39100; background-color: var(--bg-type-a);
color: #ffffff; color: var(--color-white);
} }
&[data-type="R"] { &[data-type="R"] {
background-color: #000000; background-color: var(--bg-type-r);
color: #ffffff; color: var(--color-white);
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
border: solid 2px #ffffff; border: solid 2px var(--color-white);
} }
} }
} }
span.lineChip { span.lineChip {
padding: 0.3em 0.6em; padding: 0.3em 0.6em;
min-width: 4ch; min-width: 4ch;
@ -662,4 +973,29 @@ span[data-icon] {
&[data-icon="location-searching"] { &[data-icon="location-searching"] {
mask-image: url(/md_icons/location-searching.svg); mask-image: url(/md_icons/location-searching.svg);
} }
&[data-icon="load"] {
mask-image: url(/md_icons/load.svg);
}
&[data-icon="close"] {
mask-image: url(/md_icons/close.svg);
}
&.spinner {
animation-delay: 0s;
animation-direction: normal;
animation-duration: 1s;
animation-fill-mode: none;
animation-iteration-count: infinite;
animation-name: spinner;
animation-play-state: running;
animation-timing-function: linear
}
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
} }

101
sw.js Normal file
View file

@ -0,0 +1,101 @@
const version = '0.6.4'
const consolePrefix = `[SW v${version}] `
const cacheName = `hvvstuhl-${version}`;
const contentToCache = [
'/',
'/index.html',
'/about.html',
'/main.js',
'/elevators.js',
'/style.css',
'/icons/192.png',
'/icons/512.png',
'/icons/512-maskable.png',
'/icons/512-transparent.png',
'/icons/favicon.ico',
'/icons/favicon.svg',
'/icons/favicon-maskable.svg',
'/icons/favicon-transparent.svg',
'/md_icons/bicycle.svg',
'/md_icons/braille.svg',
'/md_icons/door_sliding.svg',
'/md_icons/elevator.svg',
'/md_icons/elevator-slash.svg',
'/md_icons/fit_width.svg',
'/md_icons/height.svg',
'/md_icons/info.svg',
'/md_icons/load.svg',
'/md_icons/location.svg',
'/md_icons/location-searching.svg',
'/md_icons/speaker.svg',
'/md_icons/up-down.svg',
'/md_icons/wheelchair.svg',
'/md_icons/width.svg',
];
// Service worker Install: Cache all files
self.addEventListener("install", (e) => {
console.log(`${consolePrefix}Wird installiert....`);
e.waitUntil(
(async () => {
const cache = await caches.open(cacheName);
console.log(`${consolePrefix} Cache wird aufgebaut...`);
await cache.addAll(contentToCache);
console.log(`${consolePrefix} > FERTIG`);
console.log(`${consolePrefix} Versuche Wartezeit zu überspringen...`);
await self.skipWaiting();
console.log(`${consolePrefix} > Erfolgreich`);
})(),
);
});
// delete old caches
const deleteCache = async (key) => {
await caches.delete(key);
};
const deleteOldCaches = async () => {
const keyList = await caches.keys();
const cachesToDelete = keyList.filter((key) => key !== cacheName);
await Promise.all(cachesToDelete.map(deleteCache));
};
self.addEventListener("activate", (event) => {
event.waitUntil(
(async () => {
await deleteOldCaches();
console.log(`${consolePrefix}Versuche Clients zu beanspruchen...`);
await self.clients.claim();
console.log(`${consolePrefix} > Erfolgreich`);
})(),
);
});
// Respond with data from cache when offline
self.addEventListener("fetch", (e) => {
e.respondWith(
(async () => {
const path = new URL(e.request.url).pathname
if(contentToCache.includes(path)){
console.log(`${consolePrefix}Anfrage: ${path}`);
const r = await caches.match(e.request);
if (r) {
console.log(`${consolePrefix} > Im Cache gefunden.`);
return r;
}
const response = await fetch(e.request);
const cache = await caches.open(cacheName);
console.log(`${consolePrefix} > Nicht gefunden. Aktualisiere Cache: ${path}`);
await cache.put(e.request, response.clone());
return response;
}else{
console.log(`${consolePrefix}Anfrage übersprungen: ${e.request.url}`);
return await fetch(e.request);
}
})(),
);
});