read more text
This commit is contained in:
@@ -96,6 +96,35 @@
|
||||
return `M 0 ${-R} A ${R} ${R} 0 0 ${outerSweep} 0 ${R} A ${rx} ${R} 0 0 ${innerSweep} 0 ${-R} Z`;
|
||||
});
|
||||
let illumination = $derived(finite(phase) ? Math.round(moonIllumination(phase) * 100) : null);
|
||||
|
||||
// ─── Narrative clamp (phones only) ──────────────────────────────────────────
|
||||
// The narrative runs anywhere from two to nine lines depending on the day,
|
||||
// which on a phone means the whole page below it moves as soon as the data
|
||||
// lands. Below md the text is clamped to five lines and gets a toggle, and
|
||||
// the block reserves those five lines plus the toggle row whatever the
|
||||
// length - so the card is the same height before and after the fetch, and the
|
||||
// skeleton can match it exactly. From md up nothing is clamped.
|
||||
|
||||
let expanded = $state(false);
|
||||
let textEl = $state<HTMLParagraphElement>();
|
||||
let overflows = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
sentences; // re-measure when the day (and so the text) changes
|
||||
const el = textEl;
|
||||
if (!el || expanded) return; // measuring while open would always say "fits"
|
||||
|
||||
const measure = () => {
|
||||
// only clamped below md, where the toggle is the only way to see the rest
|
||||
const clamped = window.matchMedia('(max-width: 767px)').matches;
|
||||
overflows = clamped && el.scrollHeight - el.clientHeight > 1;
|
||||
};
|
||||
measure();
|
||||
|
||||
const observer = new ResizeObserver(measure);
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="mt-6" aria-label={m.summary_heading()}>
|
||||
@@ -115,15 +144,35 @@
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 px-4 py-3.5 lg:grid-cols-[minmax(0,1fr)_auto] lg:gap-6">
|
||||
{#if sentences.length > 0}
|
||||
<p class="text-[15px] leading-relaxed text-foreground">
|
||||
{sentences.join(' ')}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text-[15px] leading-relaxed text-muted-foreground">
|
||||
{m.summary_no_data()}
|
||||
</p>
|
||||
{/if}
|
||||
<div class="narrative">
|
||||
{#if sentences.length > 0}
|
||||
<p
|
||||
bind:this={textEl}
|
||||
class="text-[15px] leading-relaxed text-foreground"
|
||||
class:clamped={!expanded}
|
||||
>
|
||||
{sentences.join(' ')}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="text-[15px] leading-relaxed text-muted-foreground">
|
||||
{m.summary_no_data()}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<!-- Always occupies its row on phones, even when the text fits: a
|
||||
toggle that appears only sometimes is itself a layout shift. -->
|
||||
<button
|
||||
type="button"
|
||||
class="toggle mt-1 cursor-pointer text-[13px] font-semibold text-primary hover:underline"
|
||||
class:invisible={!overflows && !expanded}
|
||||
aria-hidden={!overflows && !expanded}
|
||||
tabindex={!overflows && !expanded ? -1 : 0}
|
||||
aria-expanded={expanded}
|
||||
onclick={() => (expanded = !expanded)}
|
||||
>
|
||||
{expanded ? m.summary_read_less() : m.summary_read_more()}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Sun, moon and UV: the numbers the sentence above deliberately leaves out -->
|
||||
<dl
|
||||
@@ -218,3 +267,36 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
/* Phones: exactly five lines, reserved whether the text is long or short, so
|
||||
the card's height never depends on the forecast that lands. 15px text at
|
||||
leading-relaxed (1.625) → 5 lines = 8.125em. */
|
||||
.clamped {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 5;
|
||||
line-clamp: 5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.narrative p {
|
||||
min-height: 8.125em;
|
||||
}
|
||||
|
||||
/* md and up: no clamp, no reserved height, no toggle. */
|
||||
@media (min-width: 768px) {
|
||||
.clamped {
|
||||
display: block;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.narrative p {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user