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)
collection
Expressive
Source

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.17 kB · static · 2.66 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

Live playground
onActive
idle
  1. hover · rove · click…

Hover or arrow ←/→ across the buildings — each announces its size and its lit fraction. Height is the precise read; the lit windows are impressionistic (mostly lit / half lit / dark).

labels
values
ground
readout
animate
import { CitySkyline } from "@microcharts/react/city-skyline/interactive";// teams — real values under “Sample data” below<CitySkyline  data={teams}/>
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 },];

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

omit lit for a plain bar row
labelled, the two-variable read

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

In a sentence

Team sizes across eng — Platform is largest at 46, 70% activated.

In a table cell
Platform46
Core32
Web28
In a KPI card
Platform
46heads · 70% lit
In a tab header
PlatformCoreWeb

Preview uses the public chart API only. When an interactive twin exists, it swaps in with the same props and no entrance motion - code samples stay on the static import. Placement recipes: Composition, Sizing.

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, press Escape, or press outside the chart. 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