Stop event banner size shifting
This commit is contained in:
fraxle
2026-08-02 17:06:31 +01:00
parent 249f284593
commit ec211314e0
2 changed files with 53 additions and 19 deletions
+20 -4
View File
@@ -257,18 +257,34 @@ body {
background: rgba(0, 0, 0, 0.55); background: rgba(0, 0, 0, 0.55);
} }
/* Slide stack. Every active event is rendered and every slide is placed in the
same single grid cell, so the strip's height is set by the TALLEST slide and
stays put as the rotation cycles — a one-line event followed by a message
that wraps to two lines no longer grows the banner and shunts the page. */
.event-note-stack {
display: grid;
grid-template-columns: minmax(0, 1fr);
align-items: start;
flex: 1;
min-width: 0;
}
.event-note-slide { .event-note-slide {
grid-area: 1 / 1; /* stack all slides on top of each other */
display: flex; display: flex;
align-items: baseline; align-items: baseline;
flex-wrap: wrap; flex-wrap: wrap;
gap: 4px 10px; gap: 4px 10px;
flex: 1;
min-width: 0; min-width: 0;
opacity: 1; opacity: 0; /* only .is-active is visible */
pointer-events: none;
user-select: none; /* keeps hidden copy out of a drag-selection */
transition: opacity 0.35s ease; transition: opacity 0.35s ease;
} }
.event-note-slide.is-fading { .event-note-slide.is-active {
opacity: 0; opacity: 1;
pointer-events: auto;
user-select: auto;
} }
.event-note-emoji { .event-note-emoji {
+33 -15
View File
@@ -692,9 +692,18 @@ export function UTCIForecast() {
const fmtDate = (iso) => iso const fmtDate = (iso) => iso
? new Date(iso + 'T00:00Z').toLocaleDateString('en-GB', { day: 'numeric', month: 'short', timeZone: 'UTC' }) ? new Date(iso + 'T00:00Z').toLocaleDateString('en-GB', { day: 'numeric', month: 'short', timeZone: 'UTC' })
: null; : null;
const startFmt = fmtDate(ev.start); // Date line for one event — computed per slide because every event is
const peakFmt = fmtDate(ev.peak); // rendered (see the stack below), not just the visible one.
const endFmt = fmtDate(ev.end); const dateLineFor = (e) => {
const startFmt = fmtDate(e.start);
const peakFmt = fmtDate(e.peak);
const endFmt = fmtDate(e.end);
if (!startFmt || !endFmt) return null;
if (startFmt === endFmt) return peakFmt ? `Peak ${peakFmt}` : startFmt;
return (peakFmt && peakFmt !== startFmt && peakFmt !== endFmt)
? `${startFmt} ${endFmt} · Peak ${peakFmt}`
: `${startFmt} ${endFmt}`;
};
// Warning banners take their background from the day tabs' weather // Warning banners take their background from the day tabs' weather
// palette (ev.tint, set in events/weather-checks.js) so a heat alert // palette (ev.tint, set in events/weather-checks.js) so a heat alert
// reads the same orange as a hot day tab and a rain alert the same // reads the same orange as a hot day tab and a rain alert the same
@@ -709,21 +718,30 @@ export function UTCIForecast() {
borderColor: `rgb(${ev.tint.map(c => Math.round(c * 0.72)).join(',')})`, borderColor: `rgb(${ev.tint.map(c => Math.round(c * 0.72)).join(',')})`,
}; };
})() : undefined; })() : undefined;
const dateLine = (startFmt && endFmt)
? (startFmt === endFmt
? (peakFmt ? `Peak ${peakFmt}` : startFmt)
: (peakFmt && peakFmt !== startFmt && peakFmt !== endFmt
? `${startFmt} ${endFmt} · Peak ${peakFmt}`
: `${startFmt} ${endFmt}`))
: null;
return html` return html`
<div class=${`event-note${isPriority ? ' is-priority' : ''}${noteTintStyle ? ' is-tinted' : ''}`} style=${noteTintStyle}> <div class=${`event-note${isPriority ? ' is-priority' : ''}${noteTintStyle ? ' is-tinted' : ''}`} style=${noteTintStyle}>
<span class="event-note-accent" style=${{ background: ev.color === '#fdf8ee' ? '#c8922a' : ev.color }} /> <span class="event-note-accent" style=${{ background: ev.color === '#fdf8ee' ? '#c8922a' : ev.color }} />
<div class=${`event-note-slide${noteFading ? ' is-fading' : ''}`}> ${/* Every slide is rendered, all stacked in one CSS grid cell, so the
<span class="event-note-emoji">${ev.emoji}</span> strip is always as tall as the LONGEST event's copy. Rotating to
<span class="event-note-title">${ev.title}</span> a two-line message then back no longer resizes the banner and
<span class="event-note-msg">${ev.message}</span> shunts the page up and down. Only the active slide is visible;
${dateLine && html`<span class="event-note-dates">${dateLine}</span>`} the rest sit at opacity 0 and are hidden from assistive tech. */''}
<div class="event-note-stack">
${activeEvents.map((e, i) => {
const dateLine = dateLineFor(e);
const isActive = i === noteIndex && !noteFading;
return html`
<div
key=${e.id || i}
class=${`event-note-slide${isActive ? ' is-active' : ''}`}
aria-hidden=${isActive ? undefined : 'true'}
>
<span class="event-note-emoji">${e.emoji}</span>
<span class="event-note-title">${e.title}</span>
<span class="event-note-msg">${e.message}</span>
${dateLine && html`<span class="event-note-dates">${dateLine}</span>`}
</div>`;
})}
</div> </div>
${activeEvents.length > 1 && html` ${activeEvents.length > 1 && html`
<div class="event-note-dots"> <div class="event-note-dots">