Skip to content
microcharts
ReferenceExpressiveCitySkyline

CitySkyline

CitySkyline draws each group as a building, with size as height and activation as the lit-window fraction.

encodes
building height (primary) + lit-window fraction (secondary)
precision
high height / low lit
nodes
2 per building + 1 (n ≤ 8)

CitySkyline compares a handful of groups on two variables at once: how big each one is, and how activated it is. Height is the primary, precise channel: zero-anchored bars, like a MiniBar. The lit-window fraction is a secondary, low-precision channel you read as "mostly lit, half lit, or dark", not as a number. Omit lit everywhere and you get a plain bar row.

CitySkyline
interactive · 5.1 kB · static · 2.63 kB

Install

Import & usereact/city-skyline
import { CitySkyline } from "@microcharts/react/city-skyline";// teams — real values under “Sample data” below<CitySkyline data={teams} unit="teams" title="Team sizes" />
Sample data
const teams = [  { label: "Platform", value: 46, lit: 0.7 },  { label: "Core", value: 32, lit: 0.5 },  { label: "Web", value: 28, lit: 0.9 },  { label: "API", value: 40, lit: 0.3 },  { label: "Data", value: 18, lit: 0.6 },];
Needs package + stylesheetSet up with AI

Try it

When to use it

Use it for team or region size plus an activation read, an org KPI where two variables are the story, or a per-BU comparison with utilization. For a single variable reach for MiniBar; skip it when you need a precise activation read, or when you have more than about eight groups.

Sizing

Variants

Windows appear whenever lit is present. Heights stay zero-anchored bars either way, and nothing is encoded in building width: width, roof, and ground are constants.

plain bars (no lit) + values

Edge cases

flat skyline
too short for windows

All-equal values render as a flat roofline; no variation is added to make the row look busier. Lit windows are quantized to the window count and filled bottom-up, so activation reads as a fill level. The secondary channel drops out before the primary: a building too short for one window row renders as a solid tower, and its lit still shows in the per-building readout on hover or keyboard focus.

Four homes

Accessibility

The accessible name gives the count and the tallest: "3 groups; tallest A at 46." The interactive entry roves the buildings with ←/→ or hover and announces each one. On the team demo at the top of this page, Platform reads "Platform: 46; 70% lit."

The interactive entry follows the shared interaction contract: arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or Space selects a unit — pinning its readout so it survives blur, until you select it again or press Escape. On touch, a tap pins and a drag scrubs.

Props

PropTypeNotes
data*{ label, value, lit? }[]value = height; lit = 0–1 window fraction.
labelsbooleanCategory labels under the buildings.
groundbooleanThe baseline hairline (default true).
label"none" | "value"Numeral above each building.
unitstringCategory noun for the summary (default 'groups').
bwnumberBuilding width in viewBox units (default 9).
gapnumberGap between buildings in viewBox units (default 3).
fontSizenumberType size of the value numerals, in viewBox units. Defaults from `height`.
animatebooleaninteractiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion.

Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.

Related charts