Skip to content

Components

Visibility card

The Astr zone as a pill, a sky label, the sky brightness in magnitudes per square arcsecond and five rating bars. Zone 1 lights five bars and zone 9 lights one.

Preview

VISIBILITYZone 1
Dark Sky22.00 MPSAS
VISIBILITYZone 2
Dark Sky21.85 MPSAS
VISIBILITYZone 3
Rural Sky21.60 MPSAS
VISIBILITYZone 4
Rural Sky21.30 MPSAS
VISIBILITYZone 5
Suburban Sky20.90 MPSAS
VISIBILITYZone 6
Suburban Sky20.40 MPSAS
VISIBILITYZone 7
Urban Sky19.80 MPSAS
VISIBILITYZone 8
Urban Sky19.00 MPSAS
VISIBILITYZone 9
Urban Sky18.20 MPSAS

Variants

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

5
VISIBILITYZone 5
Suburban Sky20.90 MPSAS
Variant.tsx
<AppVisibilityCard zone={5} mpsas={20.9} />

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
<AppVisibilityCard zone={5} mpsas={20.9} />
app-widgets.tsx · skyLabel
/** Sky type label as the visibility card words it. */export const skyLabel = (zone: number): string =>  zone <= 2 ? "Dark Sky" : zone <= 4 ? "Rural Sky" : zone <= 6 ? "Suburban Sky" : "Urban Sky";
app-widgets.tsx · activeBars
/** Lit bars on the rating row: zone 1 lights five, zone 9 lights one (visibility_mini_card.dart). */export const activeBars = (zone: number): number => Math.min(5, Math.max(0, 5 - Math.floor((zone - 1) / 2)));
app-widgets.tsx · AppVisibilityCard
/** VisibilityMiniCard: a 164 by 154 card with the zone pill, sky label, SQM figure and rating bars. */export function AppVisibilityCard({ zone, mpsas }: { zone: number; mpsas: number }) {  const bars = activeBars(zone);  return (    <div className="app-ui app-card is-mini">      <div className="app-card-head">        <span className="app-label-sm">VISIBILITY</span>        <span className="app-zone-pill">Zone {zone}</span>      </div>      <div className="app-vis-body">        <span className="app-title app-vis-label">{skyLabel(zone)}</span>        <span className="app-label-sm">{mpsas.toFixed(2)} MPSAS</span>      </div>      <div className="app-rating">        {Array.from({ length: 5 }, (_, i) => (          <i key={i} className={i < bars ? "on" : undefined} />        ))}      </div>    </div>  );}
app-ui.css
.app-card {  position: relative;  box-sizing: border-box;  display: flex;  flex-direction: column;  padding: var(--spacing-f13);  border-radius: var(--radius-f21);  border: var(--spacing-f1) solid var(--hair-strong);  background: var(--sheen), var(--fill);  box-shadow: var(--highlight);  backdrop-filter: blur(var(--spacing-f13));  overflow: hidden;}.app-card.is-mini { width: var(--spacing-f144); height: calc(var(--spacing-f144) + var(--spacing-f21)); flex: none; }.app-card-head { display: flex; justify-content: space-between; align-items: center; gap: var(--spacing-f8); }.app-vis-body { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--spacing-f2); }.app-vis-label { white-space: nowrap; }.app-zone-pill {  padding: var(--spacing-f3) var(--spacing-f8);  border-radius: var(--radius-full);  background: color-mix(in srgb, var(--c-accent) var(--mag-4), transparent);  border: var(--spacing-f1) solid var(--c-accent);  font-size: var(--text-s-2);  line-height: 1.3;  font-weight: 700;  color: var(--c-accent-ink);  white-space: nowrap;}.app-rating { display: flex; gap: var(--spacing-f5); }.app-rating i { display: block; flex: 1; height: var(--spacing-f5); border-radius: var(--radius-full); background: var(--track); }.app-rating i.on { background: var(--c-accent); box-shadow: var(--glow); }

API reference

React propFlutter parameterTypeDefaultMeaning
zonelightPollution.visibilityIndex1 to 9requiredThe Astr zone.
mpsaslightPollution.mpsasnumber / doublerequiredSky brightness, mag/arcsec².

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
Size--spacing-f144 by 144 + --spacing-f21AstrSpace.f144164 by 154
Radius, padding--radius-f21, --spacing-f13AstrRadius.f21, AstrSpace.f1316, 15
Surfaceglass: --sheen over space-grey-950 at --mag-1AstrGlassCardsurfaceOverlay + borderPrimary
Zone pill--radius-full, deep-space-200 at --mag-4, text deep-space-50AstrColors.deepSpace[200], [50]zonePillFill, zonePillText
Sky label--text-s1 (20)AstrType.s1title 28 scaled to 20
SQM line--text-s-2AstrType.sNeg2labelSm 10
Bars--spacing-f5 high, gap f5, lit deep-space-200AstrSpace.f55 high, gap 5, ratingBarActive