Skip to content

Components

Cloud cover graph

The hourly cloud forecast as a smoothed area over the night, with the time grid, hour labels and the pulsing NOW marker. It is the cloud layer of the conditions graph on its own.

Preview

NOW18:0021:0000:0003:0006:00

Variants

Pick a variant with the controls. The preview and both code blocks follow.

Forecast
NOW18:0021:0000:0003:0006:00
Variant.tsx
<AppCloudCoverGraph />

Implementation

The React tab is this site's own component, the same source that draws the preview, styled by the CSS below it. The Flutter tab is the Dart a screen builds it with, from the app's tokens.

Usage
<AppCloudCoverGraph /><AppCloudCoverGraph data={[80, 85, 90, 95, 92, 88, 90, 94, 96, 92, 90, 93, 95]} />
app-graphs.tsx · AppCloudCoverGraph
/** * The hourly cloud forecast: the smoothed gradient area, grid, hour labels and NOW marker of the conditions graph * with only the cloud layer. Data is a percentage at evenly spaced hours across the night. */export function AppCloudCoverGraph({ data = CLOUD_HOURLY, height = 144 + F.f34 }: { data?: readonly number[]; height?: number }) {  return <AppConditionsGraph cloudData={data} height={height} layers={{ moon: false, moonRise: false, prime: false }} />;}
app-graphs.tsx · smooth
/** Cubic segments with tension 0.2, exactly as _drawCloudCover builds them. */function smooth(points: Pt[]): string {  let d = `M${points[0][0]} ${points[0][1]}`;  for (let i = 0; i < points.length - 1; i++) {    const p0 = points[Math.max(0, i - 1)];    const p1 = points[i];    const p2 = points[i + 1];    const p3 = points[Math.min(points.length - 1, i + 2)];    const c1 = [p1[0] + (p2[0] - p0[0]) * 0.2, p1[1] + (p2[1] - p0[1]) * 0.2];    const c2 = [p2[0] - (p3[0] - p1[0]) * 0.2, p2[1] - (p3[1] - p1[1]) * 0.2];    d += ` C${c1[0]} ${c1[1]} ${c2[0]} ${c2[1]} ${p2[0]} ${p2[1]}`;  }  return d;}
app-ui.css
.app-graph { position: relative; width: 100%; }.app-graph svg { display: block; width: 100%; overflow: visible; }.app-pulse { transform-box: fill-box; transform-origin: center; animation: app-pulse 2s ease-in-out infinite; }

API reference

React propFlutter parameterTypeDefaultMeaning
datacloudCoverDatanumber[] / List<HourlyForecast>sample nightCloud cover percent at evenly spaced hours.
heightparent heightnumber160Includes 30 px for the time labels.

Design tokens

Every value on this component is a system token. The last column is what the app widget uses today, so the gap to close is visible.

RoleCSS tokenDart tokenApp widget today
Fillspace-grey-50 at --mag-3 to --mag-6AstrColors.spaceGrey[50], Mag.m3, m6Colors.white 25% to 5%
Stroke--mag-3, --spacing-f2Mag.m3, AstrSpace.f21.5
Gridspace-grey-50 at --mag-6Mag.m6white 5%
Nowsodium-airglow-400AstrColors.sodiumAirglow[400]#F97316
Smoothingcubic, tension 0.2(p2 - p0) * 0.20.2