Naast het getal in elke modulekaart staat nu hetzelfde soort lijntje als bij buiten (200x40). Zonder labels: daar is in zo'n kaart geen ruimte voor, de schaal staat al op de regel eronder en het venster is hetzelfde "vandaag" als bij het grafiekje van buiten ernaast. Per module een eigen getmeasure, gecachet onder temperature-<module> met een gedeelde TTL; fetch() heeft daarvoor een losse ttlKey gekregen, want anders zou de TTL-lookup per module mislukken en zou er helemaal niet gecachet worden. Het basisstation meet zelf temperatuur en wordt zonder module_id opgevraagd, dus die parameter is optioneel geworden. In de payload als indoor[].history; mislukt er een, dan blijft die lijst leeg en verdwijnt alleen dat lijntje, met de foutcode in errors. Een stale-vlag per kamer zou het contract nodeloos zwaar maken. De hoogte is overal 40px, ook bij een vastgezette module: gelijke hoogte houdt de kamers onderling vergelijkbaar, en een hoger lijntje zou de toch al krappe speling onder de kolom opeten. De grafiekjes voegen geen hoogte toe aan de kaarten. FakeSource houdt nu alle aanroepen bij in plaats van alleen de laatste, zodat een test de volgorde van de vier temperatuur-calls kan nalopen. Servicewerker naar v11. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
255 lines
11 KiB
CSS
255 lines
11 KiB
CSS
/* Weerscherm, ontwerp A. Vast canvas van 1133 × 744 dat via JS geschaald en gecentreerd wordt. */
|
||
|
||
:root {
|
||
--bg: #E8EDF1;
|
||
--ink: #1B2A35;
|
||
--muted: #5F707C;
|
||
--line: #C6D0D8;
|
||
--warm: #B8761C;
|
||
--cold: #3A75A3;
|
||
--rain: #2E6A99;
|
||
--sun: #D39A2C;
|
||
--good: #4E8F5A;
|
||
--bad: #B5433B;
|
||
--iconfill: #E8EDF1;
|
||
--w: 1133px;
|
||
--h: 744px;
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
html, body {
|
||
height: 100%;
|
||
overflow: hidden;
|
||
background: var(--bg);
|
||
color: var(--ink);
|
||
font-family: -apple-system, "SF Pro Display", system-ui, "Segoe UI", Roboto, sans-serif;
|
||
font-variant-numeric: tabular-nums;
|
||
-webkit-text-size-adjust: none;
|
||
-webkit-user-select: none;
|
||
user-select: none;
|
||
-webkit-touch-callout: none;
|
||
}
|
||
|
||
[hidden] { display: none !important; }
|
||
|
||
#viewport {
|
||
position: fixed;
|
||
top: env(safe-area-inset-top, 0px);
|
||
right: env(safe-area-inset-right, 0px);
|
||
bottom: env(safe-area-inset-bottom, 0px);
|
||
left: env(safe-area-inset-left, 0px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
#stage {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: var(--w);
|
||
height: var(--h);
|
||
transform-origin: top left;
|
||
display: grid;
|
||
padding: 30px 44px;
|
||
grid-template-columns: 1fr 330px;
|
||
grid-template-rows: 40px 1fr 232px;
|
||
column-gap: 56px;
|
||
background: var(--bg);
|
||
}
|
||
|
||
.num { font-variant-numeric: tabular-nums; }
|
||
.cold { color: var(--cold); }
|
||
.warm { color: var(--warm); }
|
||
.muted { color: var(--muted); }
|
||
|
||
.ic, .wind-ic { fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; width: 100%; height: 100%; display: block; }
|
||
.i-sun { stroke: var(--sun); }
|
||
.i-drop { stroke: var(--rain); }
|
||
.i-cloud { fill: var(--iconfill); }
|
||
|
||
/* Kopregel */
|
||
.a-top { grid-column: 1 / -1; display: flex; justify-content: space-between; align-items: baseline; }
|
||
.a-date { font-size: 22px; font-weight: 500; }
|
||
.a-right { display: flex; gap: 18px; align-items: baseline; }
|
||
.a-status { font-size: 15px; color: var(--warm); }
|
||
.a-upd { font-size: 15px; color: var(--muted); }
|
||
.a-upd.stale { color: var(--warm); }
|
||
.a-clock { font-size: 26px; font-weight: 300; }
|
||
|
||
/* Buiten */
|
||
.a-hero { align-self: center; }
|
||
.a-label { font-size: 22px; color: var(--muted); font-weight: 500; display: flex; gap: 14px; align-items: baseline; }
|
||
.a-trend { font-size: 17px; font-weight: 400; }
|
||
.a-temprow { display: flex; align-items: center; gap: 28px; margin: 4px 0 10px 0; }
|
||
.a-temp { font-size: 200px; font-weight: 200; letter-spacing: -.045em; line-height: .95; margin-left: -8px; }
|
||
|
||
/* Temperatuurverloop van vandaag, in de ruimte rechts van het grote getal */
|
||
.a-today { flex: 1; min-width: 0; }
|
||
.a-today[hidden] { display: none; }
|
||
.a-tcap { font-size: 15px; color: var(--muted); margin-bottom: 4px; }
|
||
.a-tspark { display: block; width: 100%; height: 96px; overflow: visible; }
|
||
.a-hum { font-size: 17px; font-weight: 400; }
|
||
.a-hum:not(:empty)::before { content: "·"; margin-right: 14px; }
|
||
.a-range { width: 560px; }
|
||
.a-track { position: relative; height: 12px; border-radius: 6px; background: linear-gradient(90deg, var(--cold), var(--warm)); }
|
||
.a-mark { position: absolute; top: 50%; width: 28px; height: 28px; border-radius: 50%; background: var(--bg); border: 5px solid var(--ink); transform: translate(-50%, -50%); transition: left .6s ease; }
|
||
.a-ends { display: flex; justify-content: space-between; margin-top: 14px; font-size: 17px; color: var(--muted); }
|
||
.a-ends b { font-size: 24px; font-weight: 600; margin-right: 6px; }
|
||
|
||
/* Luchtdruk, 24 uur. Grafiek met een y-as (hoogste/laagste waarde) en tijdlabels op de x-as. */
|
||
.a-pressure { width: 560px; margin-top: 14px; }
|
||
.a-plabel { font-size: 17px; color: var(--muted); display: flex; gap: 12px; align-items: baseline; margin-bottom: 6px; }
|
||
.a-plabel b { font-size: 20px; font-weight: 600; color: var(--ink); }
|
||
.a-pgrid { display: grid; grid-template-columns: 44px 1fr; column-gap: 8px; row-gap: 3px; }
|
||
.a-py { display: flex; flex-direction: column; justify-content: space-between; text-align: right; font-size: 13px; color: var(--muted); line-height: 1; padding: 1px 0; }
|
||
.a-px { position: relative; height: 17px; font-size: 13px; color: var(--muted); }
|
||
.a-px span { position: absolute; top: 0; white-space: nowrap; }
|
||
.a-spark { display: block; width: 100%; height: 56px; overflow: visible; }
|
||
.a-spark-base { stroke: var(--line); stroke-width: 1; }
|
||
.a-spark-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; opacity: .6; }
|
||
.a-spark-line { fill: none; stroke: var(--rain); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
|
||
|
||
/* Binnen */
|
||
.a-inside { align-self: center; }
|
||
.a-room { padding: 12px 0; border-top: 1px solid var(--line); }
|
||
.a-room:first-child { border-top: 0; }
|
||
.a-rname { font-size: 19px; color: var(--muted); display: flex; justify-content: space-between; align-items: center; }
|
||
.pill { display: inline-block; font-size: 13px; font-weight: 600; line-height: 1; padding: 5px 10px; border-radius: 999px; color: #fff; background: var(--muted); letter-spacing: .01em; white-space: nowrap; }
|
||
.pill.good { background: var(--good); }
|
||
.pill.medium { background: var(--warm); }
|
||
.pill.bad { background: var(--bad); }
|
||
.pill.unknown { background: var(--line); color: var(--muted); }
|
||
.a-rrow { display: flex; align-items: center; gap: 16px; }
|
||
.a-rtemp { font-size: 50px; font-weight: 300; line-height: 1.05; transition: font-size .25s ease; flex: none; }
|
||
/* Overal even hoog: zo zijn de kamers onderling vergelijkbaar, en groeit een vastgezette kaart niet
|
||
zo ver door dat de speling onder de kolom opraakt. */
|
||
.a-rspark { flex: 1; min-width: 0; height: 40px; overflow: visible; }
|
||
.a-room.off .a-rtemp { color: var(--muted); }
|
||
.a-room.pinned .a-rtemp { font-size: 92px; }
|
||
.a-rmm { font-size: 17px; display: flex; gap: 18px; }
|
||
.a-rmm .rv { color: var(--muted); }
|
||
|
||
/* Onderste strook */
|
||
.a-bottom { grid-column: 1 / -1; border-top: 1px solid var(--line); padding-top: 22px; display: grid; grid-template-columns: 390px 1fr; column-gap: 36px; }
|
||
.a-sub { font-size: 19px; color: var(--muted); font-weight: 500; margin-bottom: 10px; }
|
||
.a-rainrow { display: flex; justify-content: space-between; align-items: flex-end; }
|
||
.a-rbig { font-size: 22px; color: var(--rain); }
|
||
.a-rbig span { font-size: 60px; font-weight: 300; line-height: 1; }
|
||
.a-rsm { font-size: 17px; color: var(--muted); }
|
||
.a-month { font-size: 17px; margin-top: 18px; color: var(--muted); white-space: nowrap; }
|
||
.a-month b { color: var(--ink); font-weight: 600; font-size: 20px; margin-left: 4px; }
|
||
.a-bars { display: flex; gap: 6px; align-items: flex-end; }
|
||
.a-bar { display: flex; flex-direction: column; align-items: center; gap: 5px; font-size: 14px; color: var(--muted); min-width: 38px; }
|
||
.a-bar i { display: block; width: 24px; border-radius: 4px 4px 0 0; background: var(--rain); opacity: .4; transition: height .6s ease; }
|
||
.a-bar.now i { opacity: 1; }
|
||
.a-bar.now span { color: var(--ink); font-weight: 600; }
|
||
|
||
/* Verwachting */
|
||
.a-fc { display: grid; grid-template-columns: repeat(6, 1fr); text-align: center; }
|
||
.a-day { display: flex; flex-direction: column; align-items: center; gap: 6px; border-left: 1px solid var(--line); }
|
||
.a-day:first-child { border-left: 0; }
|
||
.a-dname { font-size: 19px; font-weight: 600; }
|
||
.a-icon { width: 50px; height: 50px; }
|
||
.a-hl { font-size: 28px; font-weight: 500; }
|
||
.a-hl span { font-size: 22px; color: var(--muted); font-weight: 400; margin-left: 6px; }
|
||
.a-fr { font-size: 16px; color: var(--rain); }
|
||
.a-fr span { color: var(--muted); margin-left: 6px; }
|
||
|
||
/* Meldingen */
|
||
.banner {
|
||
position: absolute;
|
||
left: 44px;
|
||
right: 44px;
|
||
top: 86px;
|
||
height: 396px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
padding: 40px 60px;
|
||
font-size: 30px;
|
||
line-height: 1.4;
|
||
color: var(--ink);
|
||
background: var(--bg);
|
||
border: 1px solid var(--line);
|
||
border-radius: 18px;
|
||
z-index: 2;
|
||
}
|
||
.banner small { display: block; margin-top: 16px; font-size: 20px; color: var(--muted); }
|
||
|
||
/* Overlays (uuroverzicht, regenradar): tik om te openen, tik erop of wacht 20s om te sluiten.
|
||
De .ho-*-chrome (balk, kop) wordt door beide overlays gedeeld. */
|
||
.overlay {
|
||
position: absolute;
|
||
left: 44px;
|
||
right: 44px;
|
||
top: 40px;
|
||
bottom: 40px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 22px;
|
||
padding: 26px 0 22px;
|
||
background: var(--bg);
|
||
border: 1px solid var(--line);
|
||
border-radius: 18px;
|
||
box-shadow: 0 12px 32px rgba(27, 42, 53, .12);
|
||
z-index: 5;
|
||
}
|
||
.overlay[hidden] { display: none; }
|
||
|
||
.ho-bar { margin: 0 30px; height: 4px; border-radius: 2px; background: var(--line); overflow: hidden; }
|
||
.ho-bar i { display: block; width: 100%; height: 100%; background: var(--rain); transform-origin: left; }
|
||
|
||
.ho-head { margin: 0 30px; display: flex; justify-content: space-between; align-items: baseline; }
|
||
.ho-date { font-size: 22px; font-weight: 500; }
|
||
.ho-hint { font-size: 16px; color: var(--muted); }
|
||
|
||
.ho-grid { display: grid; grid-template-columns: repeat(12, 1fr); text-align: center; }
|
||
|
||
.ho-temp { display: flex; flex-direction: column; gap: 6px; }
|
||
#ho-temp-nums .num { font-size: 22px; font-weight: 500; }
|
||
.ho-temp-svg { display: block; width: 100%; height: 64px; }
|
||
.ho-temp-line { fill: none; stroke: var(--ink); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
|
||
|
||
.ho-rain .ho-rbar { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 6px; height: 66px; }
|
||
.ho-rbar i { display: block; width: 14px; border-radius: 3px 3px 0 0; background: var(--rain); opacity: .55; }
|
||
.ho-rbar .num { font-size: 14px; color: var(--muted); }
|
||
|
||
.ho-wind .ho-wcell { display: flex; flex-direction: column; align-items: center; gap: 6px; }
|
||
.ho-warrow { display: flex; align-items: center; justify-content: center; width: 26px; height: 26px; color: var(--muted); }
|
||
.ho-warrow.off { font-size: 18px; }
|
||
.ho-wcell .num { font-size: 15px; color: var(--muted); }
|
||
|
||
.ho-hours span { font-size: 16px; color: var(--muted); }
|
||
|
||
/* Regenradar */
|
||
.ro-body { display: flex; gap: 56px; margin: 0 30px; min-height: 0; }
|
||
.ro-map { display: flex; align-items: center; justify-content: center; width: 560px; flex: none; }
|
||
.ro-img { height: 512px; width: auto; display: block; border-radius: 10px; }
|
||
.ro-status { font-size: 22px; color: var(--muted); }
|
||
|
||
.ro-side { display: flex; flex-direction: column; justify-content: center; }
|
||
.ro-big { font-size: 26px; color: var(--rain); }
|
||
.ro-big span { font-size: 76px; font-weight: 300; line-height: 1; }
|
||
.ro-sub { font-size: 19px; color: var(--muted); margin-bottom: 26px; }
|
||
.ro-days p { display: flex; justify-content: space-between; width: 260px; font-size: 21px; color: var(--muted); padding: 7px 0; border-top: 1px solid var(--line); }
|
||
.ro-days b { color: var(--ink); font-weight: 600; }
|
||
.ro-month { width: 260px; font-size: 19px; color: var(--muted); margin-top: 18px; }
|
||
.ro-source { font-size: 15px; color: var(--muted); margin-top: auto; padding-top: 22px; }
|
||
|
||
#rotate {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0; right: 0; bottom: 0; left: 0;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 30px;
|
||
color: var(--muted);
|
||
background: var(--bg);
|
||
z-index: 10;
|
||
}
|
||
@media (orientation: portrait) {
|
||
#rotate { display: flex; }
|
||
}
|