diff --git a/css/solar.css b/css/solar.css
index 166d837..e0ec8f0 100644
--- a/css/solar.css
+++ b/css/solar.css
@@ -19,3 +19,69 @@ input[type='range']::-webkit-slider-runnable-track {
background-size: var(--background-size, 0%) 100%;
background-repeat: no-repeat;
}
+
+/* Diagramme (Chart.js). Die Hoehe kommt aus diesem Container, nicht aus dem
+ canvas - die Charts laufen mit maintainAspectRatio: false. Auf dem Handy
+ ist der Container nur rund 300 px breit; mit 400 px Hoehe wird aus einer
+ Zeitreihe ein Hochformat, in dem die Zeitachse die kurze Seite ist. */
+.chart-container {
+ position: relative;
+ width: 100%;
+ height: 400px;
+}
+
+@media (max-width: 767.98px) {
+ .chart-container {
+ height: 250px;
+ }
+}
+
+/* Statistik-Kacheln (ajax/getStats.php). Sie lagen in nackten .col ohne
+ Breitenangabe - Bootstrap laesst sie dann nach Textlaenge umbrechen, mal
+ zwei, mal drei je Zeile und unterschiedlich breit. Die Breite kommt jetzt
+ aus row-cols-* in getStats.php; hier sorgt die volle Kartenhoehe dafuer,
+ dass die Werte trotz ein- oder zweizeiliger Titel auf einer Linie
+ stehen. */
+.card.stats {
+ height: 100%;
+}
+
+.card.stats .card-body {
+ flex: 1 1 auto;
+}
+
+/* Auf dem Handy gehen von 375 px rund 75 an Innenabstaende verloren, und
+ zwischen den Karten stehen 24 px. Beides wird hier enger gefasst - nur
+ im Inhaltsbereich, damit Seitenleiste, Kopfzeile und Modals unberuehrt
+ bleiben. Die Abstandsklassen von Bootstrap (mb-4) tragen !important,
+ deshalb steht es auch hier. */
+@media (max-width: 767.98px) {
+ .app-content {
+ padding: 0 .25rem;
+ }
+
+ .app-content-header {
+ padding: .5rem .25rem;
+ }
+
+ .app-content .row {
+ --bs-gutter-x: .5rem;
+ }
+
+ .app-content .card {
+ margin-bottom: .75rem !important;
+ }
+
+ /* Nur der Standardwert wird kleiner; eigene Angaben im Markup (p-0 fuer
+ das Realtime-SVG, p-2 fuer die Statistik) gelten weiter. */
+ .app-content .card-body {
+ padding: .5rem;
+ }
+
+ /* Eine Kachel ist auf dem Handy rund 150 px breit. In voller Schrift
+ passt "Verbrauchsersparnis" dort nicht in eine Zeile und wird mitten
+ im Wort umbrochen. */
+ .card.stats .card-body {
+ font-size: .8rem;
+ }
+}
diff --git a/js/solar/common.js b/js/solar/common.js
index 3d8dc5f..63fbdcc 100644
--- a/js/solar/common.js
+++ b/js/solar/common.js
@@ -156,12 +156,60 @@ function bindModalSlider(slider, span, label) {
}
/** Meteoblue-Meteogramm einhängen, sofern die Seite einen Platz dafür hat. */
+// Mindestbreite, in der das Meteogramm von meteoblue geladen wird. Der
+// Anbieter legt sein Bild einmal beim Laden fest und zeichnet es bei einer
+// Breitenaenderung nicht neu; unterhalb von etwa 600 px ueberlagern sich
+// Wochentage und Stundenachse zu einem unlesbaren Streifen. Auf schmalen
+// Schirmen wird es deshalb in dieser Breite geladen und als Ganzes
+// verkleinert - dieselbe Ansicht wie am Rechner, nur kleiner. Ist die Karte
+// breiter, wird gleich in Kartenbreite geladen und nichts skaliert.
+const METEOGRAM_BREITE = 640;
+const METEOGRAM_HOEHE = 500;
+
function mountMeteogram() {
- const target = document.getElementById("meteogram");
- if (!target) return;
+ const ziel = document.getElementById("meteogram");
+ if (!ziel) return;
// loading="lazy": das Meteogramm ist ein vollstaendiges Fremd-Dokument und
// liegt unterhalb der Falz - es wird erst geladen, wenn man hinscrollt.
- target.innerHTML = "";
+ const stil = window.getComputedStyle(ziel);
+ const innenBreite = ziel.clientWidth - parseFloat(stil.paddingLeft) - parseFloat(stil.paddingRight);
+ const ladeBreite = Math.max(METEOGRAM_BREITE, Math.round(innenBreite) || 0);
+ ziel.innerHTML =
+ "
"
+ + ""
+ + "
"
+ + ""
+ + "";
+
+ const buehne = ziel.querySelector(".meteogram-buehne");
+ const rahmen = buehne.querySelector("iframe");
+ // Zuschnitt und Skalierung stehen hier und nicht im Stylesheet: sonst
+ // haengt die Darstellung an einer womoeglich veralteten solar.css im
+ // Browser-Cache, und der breit geladene Rahmen ragte ungeschnitten aus
+ // der Karte heraus.
+ buehne.style.overflow = "hidden";
+ rahmen.style.display = "block";
+ rahmen.style.transformOrigin = "0 0";
+ let letzteBreite = 0;
+ const anpassen = function () {
+ const breite = buehne.clientWidth;
+ if (breite === 0 || breite === letzteBreite) return;
+ letzteBreite = breite;
+ const faktor = Math.min(1, breite / ladeBreite);
+ rahmen.style.transform = "scale(" + faktor + ")";
+ buehne.style.height = Math.round(METEOGRAM_HOEHE * faktor) + "px";
+ };
+ anpassen();
+ // Nur auf Breitenaenderungen reagieren - die Hoehe setzt anpassen() selbst,
+ // sonst riefe sich der Beobachter gegenseitig auf.
+ if (window.ResizeObserver) {
+ new ResizeObserver(anpassen).observe(buehne);
+ } else {
+ window.addEventListener("resize", anpassen);
+ }
}
/**
diff --git a/restricted/charts.html b/restricted/charts.html
index 90e7652..92d4dfc 100644
--- a/restricted/charts.html
+++ b/restricted/charts.html
@@ -48,7 +48,11 @@
// bei DPR 3 rund 75 %.
Chart.defaults.devicePixelRatio = Math.min(1.5, window.devicePixelRatio || 1);
- Chart.defaults.font.size = 10;
+ // Auf schmalen Schirmen ist die Zeichenflaeche nur rund 300 px breit -
+ // 10 px Schrift nehmen dort zu viel davon weg. Gleiche Schwelle wie die
+ // flachere Diagrammhoehe in css/solar.css.
+ const schmalerSchirm = window.matchMedia("(max-width: 767.98px)").matches;
+ Chart.defaults.font.size = schmalerSchirm ? 8 : 10;
Chart.defaults.animations = false;
Chart.defaults.animation = false;
Chart.defaults.plugins.legend.position = "bottom";
diff --git a/restricted/header.php b/restricted/header.php
index 3db275b..898c148 100644
--- a/restricted/header.php
+++ b/restricted/header.php
@@ -29,7 +29,7 @@ if (!isset($_GET["floor"]))
-
+ " />
diff --git a/restricted/heat.php b/restricted/heat.php
index 6039d0a..c44519a 100644
--- a/restricted/heat.php
+++ b/restricted/heat.php
@@ -60,8 +60,8 @@