Charts/Base
Bar Gauge
<BarGauge /> · 14 examples
A filled bar for each value against a known range, coloured by how close it is to trouble.
Overview
A bar chart answers how big is each thing? A bar gauge answers a different and more anxious question: how full is each thing? Disk at 83%. Error budget at 12%. A queue holding 9,400 of a possible 10,000. The bar has a known start and a known end, and the interesting information is where the fill sits between them and what colour it has turned.
The idea comes from the instrument panel: the fuel gauge, the thermometer tube, the signal meter on an old stereo. All of them exist because a number is slow to read and a length is quick. The dashboards of the last decade took that one trick and put forty of them on a screen, which is why you will find it described as a bar gauge in monitoring tools and bullet graph by people who have read Stephen Few.
In zopra-charts there are three looks. The default gradient shows which thresholds a bar has crossed. The LCD style chops the bar into discrete cells, like a meter in a recording studio, which is easier to count at a distance. The basic style is a quiet rectangle for dashboards that already have enough going on. All three share one idea: the range is fixed, and the bar is honest about it.
Props
API
<BarGauge> shows one or more values as bars filled between a minimum and a maximum, coloured by thresholds or a colour scheme. If you have used Grafana's Bar Gauge panel, see Coming from Grafana.
use zopra_charts::*;<BarGauge>- Where the bars come from
- Display options
- Range, colour and thresholds
- Value text
- Enums and types
- Coming from Grafana
Not sure which option fits? See the guide.
<BarGauge>
| Prop | Type | Default |
|---|---|---|
data | Vec<T>, Signal<Vec<T>>, Frame<N>, Signal<Frame<N>>, or your own Dataset | required |
name | Fn(&T) -> &str | field names for a Frame |
value | Fn(&T) -> f64 | field values for a Frame |
reduce | Reduce | one bar per row with value; the last value of each field for a Frame |
fields | &'static [&'static str] | every field of a Frame |
display | BarDisplay | Gradient |
value_mode | ValueMode | Color |
name_placement | NamePlacement | Auto |
text_mode | TextMode | Auto |
orientation | BarOrientation | Auto |
show_unfilled | bool | true |
sizing | Sizing | Auto |
title_size, value_size | Pixels | fitted to the space |
min, max | f64 | 0. and 100. |
thresholds | Thresholds | your theme's good colour, then its bad colour from 80 |
color | GaugeColor | Thresholds |
format | Fn(f64) -> SharedString | none |
decimals | usize | up to 3, trailing zeros dropped |
legend | Legend | Legend::hidden() |
class | Tailwind classes | fills its parent |
key | anything Into<ElementId> | – |
Where the bars come from
| Data | Props | Bars |
|---|---|---|
| rows of your own type | name and value | one per row |
| rows of your own type | value and reduce={Reduce::Calc(..)} | one bar summarising every row |
a Frame | none | one per field, showing its last value |
a Frame | reduce={Reduce::Calc(Calc::Mean)} | one per field, summarised with the calc |
a Frame | reduce={Reduce::All { limit: Some(20) }} | one per cell, row by row, up to the limit |
a Frame | fields={&["api", "web"]} | only those fields |
name returns a borrowed string, so there's nothing to clone:
<BarGauge data={disks} name={|d: &Disk| d.mount.as_str()} value={|d| d.used} />Reduce
| Variant | Meaning |
|---|---|
Reduce::Calc(Calc) | one value per field, using any Calc: Last, Mean, Max, … |
Reduce::All { limit } | every value; limit: None shows them all |
Display options
| Prop | Effect |
|---|---|
display | Gradient fills the bar with a blend of the colours it passes through. Lcd lights up separate cells. Basic is a flat bar with a solid edge. |
orientation | Horizontal stacks the bars as rows. Vertical lays them out as columns. Auto picks columns when the area is wider than it is tall. |
name_placement | Top puts the name above the bar, Left beside it, Hidden hides it. Auto places it on top when the row is taller than 40px, otherwise on the left. Vertical bars always show the name underneath. |
text_mode | Value, Name, ValueAndName or None. Auto shows the name when there's more than one bar or you provide a name accessor. |
value_mode | Color draws the value in the bar's colour, Text in the normal text colour, Hidden hides it. |
show_unfilled | Draws the empty part of the track. Ignored in Lcd mode. |
sizing | Auto divides the space evenly, with rows between 16 and 300px tall and columns at least 8px wide. Sizing::Manual { min_width, min_height, max_height } sets those limits yourself. |
title_size, value_size | Fixed text sizes instead of sizes fitted to the space. |
When the bars need more room than the area has, the ones that don't fit are cut off.
Range, colour and thresholds
The bar's length is its value's position between min and max. Values above max fill the bar, and values below min or missing (NaN) show only a sliver.
GaugeColor
| Variant | Colour of each bar |
|---|---|
GaugeColor::Thresholds | the threshold step the value has reached |
GaugeColor::Palette | the next colour from your theme's palette, one per bar |
GaugeColor::Scheme(Scheme) | the value's position between min and max on a colour scheme |
GaugeColor::Fixed(Hsla) | one colour for every bar |
In Gradient mode, thresholds and schemes also blend along the bar: with thresholds the bar passes through each step it has reached, and with a scheme it shows the whole scheme along the track. In Lcd mode, each cell takes the colour for its own position.
Thresholds
static STEPS: LazyLock<[(f64, Hsla); 3]> =
LazyLock::new(|| [(f64::NEG_INFINITY, rgb(0x40a02b).into()), (60., rgb(0xdf8e1d).into()), (85., rgb(0xd20f39).into())]);
thresholds={Thresholds::absolute(&STEPS[..])}| Constructor | Step values are |
|---|---|
Thresholds::absolute(steps) | values in the same units as the data |
Thresholds::percentage(steps) | percentages of the min–max range, from 0 to 100 |
The first step is the base colour, used for everything below the second step, so its value doesn't matter. Each later step applies from its value upwards. Steps take a Vec or a &'static slice; a static slice costs nothing to rebuild on every render.
Value text
The value is shown using format if you set it, otherwise with decimals, otherwise with up to three decimals and the trailing zeros dropped.
format={|v| format!("{v:.0} GiB").into()}Enums and types
| Type | Values |
|---|---|
BarDisplay | Gradient (default), Lcd, Basic |
ValueMode | Color (default), Text, Hidden |
NamePlacement | Auto (default), Top, Left, Hidden |
TextMode | Auto (default), ValueAndName, Value, Name, None |
BarOrientation | Auto (default), Horizontal, Vertical |
Sizing | Auto (default), Manual { min_width, min_height, max_height } |
Reduce | Calc(Calc), All { limit } |
ThresholdsMode | Absolute, Percentage |
GaugeColor | Thresholds (default), Palette, Scheme(Scheme), Fixed(Hsla) |
Coming from Grafana
| Grafana option | Here |
|---|---|
| Display mode | display |
| Value display | value_mode |
| Name placement | name_placement |
| Text mode | text_mode |
| Show unfilled area | show_unfilled |
| Bar size, Min width, Min height, Max height | sizing |
| Orientation | orientation |
| Value options: Calculation / All values / Limit | reduce |
| Value options: Fields | fields |
| Text size: title / value | title_size, value_size |
| Min, Max | min, max |
| Thresholds (absolute / percentage) | thresholds |
| Color scheme | color |
| Unit, Decimals | format, decimals |
| Legend | legend |
Not available here: value mappings, data links, and the colour schemes that need Grafana's field-colour registry. Use GaugeColor::Scheme with any scheme from your theme instead.
Run them
14 examples
Press Run to load an example into the live render at the top of the page. Open one to read its code.
How to read it
- 01Look at the colour before the length. Green or red tells you whether to keep reading.
- 02Then compare lengths down the list. Bars share one scale, so a longer bar is a fuller one, across rows or columns.
- 03In LCD mode, count lit cells: it is easier at a distance than judging a smooth edge.
- 04Hover a bar for its exact value. The name sits with each bar, and the text can be set to show names, values or both.
When to use it, and when not to
Reach for it when
- Each value has a known range and you care how full it is: disk, memory, quota, SLO budget, queue against capacity.
- You are comparing many things against the same limits, such as forty hosts with the same disk size.
- The reader needs a verdict (fine, watch it, fix it) faster than they need a number. Colour thresholds do the work.
- You have a table of series and want the latest value, or the mean or the peak, of each.
Skip it when
- The values have no natural maximum. A bar with an invented max is a bar chart with a confusing coat on.
- The point is how a value changed. Use Trend or Time Series. One gauge is one moment.
- You need to compare sizes rather than fullness. A plain bar chart is the honest one there.
- A single value with a target and context deserves a Bullet chart, which has room for the comparative marker.
The math behind it
Fill
Each bar has a value and the gauge has a range set by min and max. The filled fraction is the position of the value inside the range. It is capped at one on the high side, and a value that is not a number is drawn empty.
The bar's length in pixels is the available extent along the gauge's axis times that fraction. The fraction is the same whether the bars are drawn as rows or columns. Only the axis changes.
Thresholds
Colours come from threshold steps, each a value and a colour, with the first step covering everything below the second. Thresholds::absolute states steps in the units of the data. Thresholds::percentage states them as percentages of the range, so a step is at the value below, and the thresholds follow min and max when those change.
The default gauge turns from the theme's good colour to its bad colour at 80. The step's position along the bar is its fraction , which is where the gradient marks it.
LCD cells
The LCD style divides the extent into cells of about 12 px including the gap. Cell stands for the value and takes its colour from that value, so the bar changes colour exactly where the thresholds are. It is lit when that value is at or below the current one, and unlit cells are drawn faintly.
The consequence is that LCD bars are quantised. A value that is 41% of the way across lights 41% of the cells rounded down to a whole cell, and that is the price for being countable.
Real life usage
Forty-one hosts and a quiet red bar
Gustavo ran the storage fleet for a university research cluster in Valparaiso: forty-one machines, each with a 12-terabyte scratch disk, each used by a rotating cast of graduate students who regarded deletion as a thing that happened to other people's files.
The monitoring was a table. Forty-one rows, hostnames in one column, used percentage in the next. Every Monday Gustavo scrolled it, squinted at the numbers, and tried to remember which of them were considered bad. A rule in the alerting system paged him at 95%, which is roughly like a fire alarm that goes off when the building is already hot.
In February a disk filled to 100% in the middle of a three-day simulation. The run was lost. The student who owned it was gracious, in the way people are when they plan to mention it in their thesis acknowledgements.
Gustavo wanted the table to look like what it was, a list of things with a limit. He used <BarGauge> from zopra-charts: one bar per host, fed from the same time series he already collected, reduced to the latest value of each field, with percentage thresholds at 70 for amber and 85 for red so the colours moved if a disk was ever resized.
He picked the LCD display because the wall monitor was across the room, and the lit cells were countable from the door. The default horizontal rows with names on the left fit all forty-one on one screen.
Within a week the wall told a story the table had hidden. Eleven bars were amber, and three of those had been climbing by roughly 4 per cent a day. Gustavo emailed those three owners with a screenshot and a date, and not a word more.
No simulation has been lost since. The monitor in the corridor is now referred to as the thermometer, and students walk by and, with a kind of fond dread, count their cells.
Pitfalls
- Invented maximum. If
maxis a number you made up, every bar's fullness is a statement about your imagination. Use a real capacity. - Thresholds that do not follow the range. Absolute thresholds stay put when you change
minandmax. Percentage ones move with them. Pick the one you mean. - Pale bars with white text. Use
value_mode={ValueMode::Text}so the numbers stay readable on a light colour. - Palette colours used as a verdict.
GaugeColor::Palettetells bars apart, it does not judge them. Reserve red for bad. - Building threshold lists in render. A
vec![..]in your component is rebuilt every time. Put them in astatic.
From around the web