GradeProfile
GradeProfile colors each segment of a route by its grade and labels the steepest pitch.
- encodes
- colour = quantized grade bin; area = elevation ridge; x = distance
- precision
- medium — a difficulty read; the readout gives the exact grade
- nodes
- 1 quad per segment + ridge (+ seat-gated summit tick)
A stage climbs 67 m over 900, and the wall sits at 800. GradeProfile shows where. Each segment between two points is filled from the baseline and colored by its grade, from gentle to brutal, with the elevation ridge riding on top, and the steepest pitch is labeled so the hardest moment isn't buried.
How to read it — color is a quantized grade bin, never a smooth ramp: flats and every descent take the faint band,
then a mid tone, the negative color, and the darkest fill for the brutal pitches. The default bins of [3, 6, 10]
percent match how climbs are described, so a route reads as gentle, rolling, steep, or wall. Height is the elevation
ridge for shape; the encoded channel is the color, and the interactive readout gives the exact grade. Below 72 pixels
wide the four bins collapse to a flat-versus-climb read, because the finer classes stop being separable there.
d and elev must share a unit (both metres, say) so that grade — rise ÷ run — is a true percent. Distance need only
be monotonic; the values themselves can be any scale.
Install
import { GradeProfile } from "@microcharts/react/grade-profile";// trail — real values under “Sample data” below<GradeProfile data={trail} format={(n) => `${n} m`} title="Queen stage" />Sample data
const trail = [ { d: 0, elev: 800 }, { d: 100, elev: 809 }, { d: 250, elev: 812 }, { d: 350, elev: 817 }, { d: 500, elev: 835 }, { d: 700, elev: 833 }, { d: 900, elev: 865 },];Try it
When to use it
Use it for route and climb profiles in cycling, running, and hiking, and to show where the hard pitches fall. For a single elevation series use Sparkline; distance must be monotonic, so an unordered track won't work.
Sizing
Variants
format accepts Intl.NumberFormatOptions (with a locale) or a formatting function, and applies to distance and
elevation numbers in the summary and readout — not grade. Grades are always percent-encoded; the locale still decides
the decimal mark: the alpine profile above reads "12.000, 1.250 gain; steepest 12,3% at 3.500." in German.
Edge cases
A descent-only route has no climb to grade, so no pitch is emphasized and the summary reports "600 m, no real climb." Descents always take the gentlest color rather than borrowing a climb's, because climbing difficulty is what the chart grades. A non-finite elevation breaks the profile into a gap: the segments touching it drop out and the ridge splits, so a missing reading never invents a grade. A flat route reports the "no real climb" sentence too, and a single point has no segment to grade at all, so its accessible name falls back to "No data."
Four homes
Accessibility
The accessible name gives the distance, the total climb, and where the hardest pitch falls: "900, 67 gain; steepest
16% at 800." None of it depends on color. The interactive entry announces each pitch as you rove it (←/→), with
the true grade and the cumulative climb.
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
| Prop | Type | Notes |
|---|---|---|
| data* | { d: number; elev: number }[] | Distance + elevation, monotonic in d, same unit so grade is a true percent. |
| bins | [number, number, number] | Ascending grade-% thresholds (always percent) that quantize the four difficulty bins. |
| format | Intl.NumberFormatOptions | (n) => string | Formats distance and elevation in the summary and readout; grades always render as percent. |
| label | "max" | "none" | Mark the steepest pitch, or render the profile alone. |
| animate | boolean | interactiveOpt-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.