5.1.1
Tidy simple view card spacing and locations. Make simple view fonts bigger Fix quick view memory
This commit is contained in:
+28
-12
@@ -2410,15 +2410,23 @@
|
||||
}
|
||||
|
||||
.fsc-card {
|
||||
/* One fixed width at every hour interval — a 4h card is not a wider card,
|
||||
just a card with more space either side of it. It spans 2*bucket grid
|
||||
tracks (see fscPlot in app.js) and is centred within that span, so it
|
||||
stays over its landing hour and keeps lining up with the curve below
|
||||
however wide the span gets. Must match FSC_CARD_W in app.js. */
|
||||
width: 92px;
|
||||
/* Kept alongside the fixed width: .fsc-feel is nowrap, so a long band label
|
||||
("Very Strong Heat Stress") gives the card a ~146px min-content. Left to
|
||||
the automatic minimum that can push the tracks it spans wider than their
|
||||
neighbours', and the grid stops being uniform — which is the one thing
|
||||
the connector-line maths (hourAt in app.js) assumes. */
|
||||
min-width: 0;
|
||||
justify-self: center;
|
||||
text-align: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 10px 8px 12px;
|
||||
/* 2px each side = 4px between neighbouring cards. Done with margin rather
|
||||
than a grid gap so the cards stay centred on their landing hour and keep
|
||||
lining up with the curve below. */
|
||||
margin: 0 2px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -2433,11 +2441,14 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Same display serif as the hour cell in the Detailed and Table views
|
||||
(.utci-time) — the time reads as the same thing in all three. Now the
|
||||
cards are a fixed 92px this can carry the full 16px too. */
|
||||
.fsc-time {
|
||||
font-family: JetBrains Mono, monospace;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #4a3218;
|
||||
font-family: Fraunces, serif;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #1e1208;
|
||||
line-height: 1;
|
||||
}
|
||||
.fsc-card--now .fsc-time {
|
||||
@@ -2482,11 +2493,14 @@
|
||||
}
|
||||
.fsc-meta-row {
|
||||
font-family: Manrope, sans-serif;
|
||||
font-size: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
line-height: 1.25;
|
||||
color: #6b5230;
|
||||
opacity: 0.8;
|
||||
/* Was 0.8 at 10px, which read as grey-on-cream mush. The wider cards have
|
||||
the room, and these three lines are the only numbers on the card besides
|
||||
the temperature — they should be legible, not decorative. */
|
||||
opacity: 0.9;
|
||||
}
|
||||
.fsc-card--now .fsc-meta-row { opacity: 1; color: #4a3218; }
|
||||
.fsc-meta-rain { color: #2a6a90; }
|
||||
@@ -2538,7 +2552,9 @@
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.fsc-card {
|
||||
min-width: 55px;
|
||||
/* Width holds at 92px on mobile too — the row scrolls horizontally, so
|
||||
narrowing the cards buys nothing and costs legibility. Only the inner
|
||||
padding tightens. */
|
||||
padding: 8px 4px 10px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
+37
-17
@@ -371,12 +371,18 @@ export function UTCIForecast() {
|
||||
}, []);
|
||||
|
||||
const [simpleTemp, setSimpleTemp] = useState('utciAdj');
|
||||
// Skips the very first run so a page refresh keeps the saved Simple /
|
||||
// Table / Detailed choice — the profile-driven jump to the table below
|
||||
// is meant for an actual profile *switch*, not for restoring state.
|
||||
const profileViewFirstRun = useRef(true);
|
||||
useEffect(() => {
|
||||
if (activeProfile === 'vehicle' || (activeProfile === 'outdoors' && outdoorsVariant === 'driver')) setSimpleTemp('vehicleT');
|
||||
else if (activeProfile === 'home' || (activeProfile === 'outdoors' && outdoorsVariant === 'office')) setSimpleTemp(indoorManaged ? 'managedT' : 'indoorT');
|
||||
else if (activeProfile === 'pets') setSimpleTemp('furSurfaceT');
|
||||
else setSimpleTemp('utciAdj');
|
||||
if (['alltemps', 'showall', 'custom', 'farming', 'construction', 'market', 'windowcleaning', 'office'].includes(activeProfile)) {
|
||||
if (profileViewFirstRun.current) {
|
||||
profileViewFirstRun.current = false;
|
||||
} else if (['alltemps', 'showall', 'custom', 'farming', 'construction', 'market', 'windowcleaning', 'office'].includes(activeProfile)) {
|
||||
setForecastView('table');
|
||||
}
|
||||
}, [activeProfile, outdoorsVariant]);
|
||||
@@ -639,15 +645,18 @@ export function UTCIForecast() {
|
||||
// The grid is measured in HALF-hour tracks, because a card centred on its
|
||||
// landing hour starts on a half-hour boundary. All tracks are identical,
|
||||
// and each card SPANS 2*bucket of them (rather than sitting in one track
|
||||
// at width:400%) — spanning divides a card's intrinsic width across the
|
||||
// tracks it covers, so the grid's max-content width stays close to what
|
||||
// it was and mobile doesn't gain a load of extra horizontal scroll.
|
||||
// at width:400%). The card itself is a fixed width centred in that span,
|
||||
// so a longer bucket buys spacing between cards rather than a fatter card.
|
||||
//
|
||||
// tracks = [ pad ][ hour 0 ][ hour 1 ] … [ hour H-1 ][ pad ]
|
||||
// pad = bucket half-hours (>= the (bucket-1)/2 h overhang, + breathing room)
|
||||
// hour j -> centre at (bucket + 2j + 1) / total
|
||||
// card -> spans 2*bucket tracks, starting one half-hour after 2j
|
||||
// => centre = 2j + 1 + bucket == hour j's centre ✓
|
||||
// Quick-view card width, and the minimum clearance between two cards. Kept
|
||||
// here rather than only in CSS because the grid track sizing is derived from
|
||||
// them; .fsc-card sets the same width.
|
||||
const FSC_CARD_W = 92, FSC_CARD_GAP = 4;
|
||||
const fscPlot = (() => {
|
||||
if (!tableRows.length) return null;
|
||||
const cards = tableRows.length;
|
||||
@@ -660,10 +669,16 @@ export function UTCIForecast() {
|
||||
const landing = [];
|
||||
let j = 0;
|
||||
for (const r of tableRows) { landing.push(j); j += r.isoHours ? r.isoHours.length : 1; }
|
||||
// A card spans 2*bucket tracks, so this keeps its 55px minimum.
|
||||
const unit = 55 / (2 * bucket);
|
||||
// Every card is a fixed FSC_CARD_W wide at every interval (see .fsc-card),
|
||||
// centred in the 2*bucket tracks it spans. So the tracks only have to be
|
||||
// wide enough that neighbouring cards clear each other: a span of at least
|
||||
// card + gutter. At 1h that is the tight case; at 3h/4h the same rule
|
||||
// leaves a wider gap between cards, which is what the extra hours per card
|
||||
// should look like. The 1fr half of the minmax lets the gaps grow further
|
||||
// on a wide screen — the cards never do.
|
||||
const unit = (FSC_CARD_W + FSC_CARD_GAP) / (2 * bucket);
|
||||
return {
|
||||
hourly, hours, bucket, landing,
|
||||
hours, bucket, landing,
|
||||
cols: `repeat(${total}, minmax(${unit.toFixed(2)}px, 1fr))`,
|
||||
// Fraction of the track width at which hour j's data point sits.
|
||||
hourAt: j2 => (bucket + 2 * j2 + 1) / total,
|
||||
@@ -1349,20 +1364,25 @@ export function UTCIForecast() {
|
||||
${(() => {
|
||||
if (!fscPlot) return null;
|
||||
const fscSvgW = 1000, fscSvgH = 80;
|
||||
const fscSrc = fscPlot.hourly;
|
||||
// The curve plots the SAME rows the cards do. It used to plot the
|
||||
// raw hourly series instead, which agrees with the cards at 1h and
|
||||
// diverges at every longer interval: a card shows its bucket's
|
||||
// aggregate, so an "8am 24°" card (the 8–11am mean) sat above a
|
||||
// curve drawn at 8am's own 14.5°, and its connector pointed at a
|
||||
// height the card never claimed. One series, one number.
|
||||
const fscSrc = tableRows;
|
||||
// Fixed scale: bottom = Freezing band bottom (−10) − 10, top = Danger start (44) + 10
|
||||
const fscMin = -15, fscMax = 45;
|
||||
const toY = t => fscSvgH - ((t - fscMin) / (fscMax - fscMin)) * fscSvgH;
|
||||
const getT = r => r[simpleTemp] ?? r.utciAdj;
|
||||
// Same hour->x mapping the cards grid uses (see fscPlot above), so a
|
||||
// card's centre and its hour's data point are the same x by
|
||||
// construction — every hour of the day stays on screen.
|
||||
const fscHourX = j => fscPlot.hourAt(j) * fscSvgW;
|
||||
const fscStopPct = j => (fscPlot.hourAt(j) * 100).toFixed(1);
|
||||
const fscAllPts = fscSrc.map((r, j) => ({ x: fscHourX(j), y: toY(getT(r)) }));
|
||||
// Connector points — one per card, planted on its landing hour's data
|
||||
// point, which is exactly where that card is centred.
|
||||
const fscPts = fscPlot.landing.map(j => fscAllPts[Math.min(j, fscAllPts.length - 1)]);
|
||||
// x of card i — the landing hour it is centred on, via the same
|
||||
// mapping that placed the card in the grid, so a point and its
|
||||
// card share an x by construction.
|
||||
const fscCardX = i => fscPlot.hourAt(fscPlot.landing[i]) * fscSvgW;
|
||||
const fscStopPct = i => (fscPlot.hourAt(fscPlot.landing[i]) * 100).toFixed(1);
|
||||
const fscAllPts = fscSrc.map((r, i) => ({ x: fscCardX(i), y: toY(getT(r)) }));
|
||||
// One point per card now, so the connectors ARE the data points.
|
||||
const fscPts = fscAllPts;
|
||||
const fscNowFlags = tableRows.map(r => r.isoHours ? r.isoHours.includes(nowLocalISO) : r.iso.slice(0, 13) === nowLocalISO);
|
||||
// Flat runs from each edge into the first/last hour, so the fill still
|
||||
// covers the inset spacer strips at both ends.
|
||||
|
||||
Reference in New Issue
Block a user