GrChartBullet
Overview
When to take it
- a metric with thresholds that are already in the data —
warningandcriticalarrive from the backend and are usually drawn nowhere: “bad” is visible, “how bad” is not; - the plan and the fact side by side — the notch of the target and the bar of the value are read at a glance, without subtracting in one’s head;
- several metrics in a row — a bullet takes up a line, and a dozen metrics stand as a list comparable vertically;
- room is expensive — a card, a table row, the header of a dashboard: the same meaning as a dial in a fifth of the area;
- the quantity may be missing —
value: nulldraws the target and the ranges without a bar rather than a zero.
When to take something else
| Need | Take |
|---|---|
| Compare the quantities of several categories with one another | GrChartBar |
| Show the course of a quantity over time | GrChartLine |
| Show the share of completion of a single task | GrProgressCircle |
| Show one number large, with a trend | GrStatistic |
Examples 4 each with a live demo and its code
Latency objectives, one service per row
rangespaint the track in bands — good, tolerable, bad — andtargetdraws the objective across it: «182» alone says little, «182 of 200, inside the good band» says it all. A bullet takes one line, so a table of services fits where a single gauge would stand. A service without traffic getsvalue: null: the mark and the bands stay, the bar does not — no data is not zero.value-clickopens the service.Latency objectives · p95, last hourThe mark is the objective; the bands are good, tolerable and bad Checkout API182 msChart data Metric Value Target Checkout API 182 200 Ranges: 0 to 200; 200 to 300; 300 to 400 Search96 msChart data Metric Value Target Search 96 150 Ranges: 0 to 150; 150 to 250; 250 to 300 Invoices341 msChart data Metric Value Target Invoices 341 250 Ranges: 0 to 250; 250 to 350; 350 to 450 Exportsno trafficChart data Metric Value Target Exports no value 900 Ranges: 0 to 900; 900 to 1,500; 1,500 to 2,000 Click a bar to open the service.
Quota, where more is better
The default bands run from good to bad as values grow — right for latency, wrong for a quota.
range-colorspaints them band by band: red under 70 %, amber up to the quota, green above it.colorsets the tone of the value bar, andsize="xs"keeps four people in a small card.Quota attainment · OctoberTarget is 100 % of the personal quota Grace Wu114%Chart data Metric Value Target Grace Wu 1.14 1.00 Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30 Omar Haddad96%Chart data Metric Value Target Omar Haddad 0.96 1.00 Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30 Ines Novak81%Chart data Metric Value Target Ines Novak 0.81 1.00 Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30 Linus Ray58%Chart data Metric Value Target Linus Ray 0.58 1.00 Ranges: 0.00 to 0.70; 0.70 to 1.00; 1.00 to 1.30 A budget that is already overspent
Marketing spent more than its whole budget: the bar stops at
maxwith an overflow marker instead of being clipped silently, and the real figure stays in the tooltip, the hidden table and the announcement. Thetooltipslot adds the share of the budget;aria-descriptionexplains what the mark means.Budgets after nine monthsThe mark is the share of the year gone — 75 % of the budget Engineering€1420k of €2000kChart data Metric Value Target Engineering 1,420 1,500 Ranges: 0 to 1,500; 1,500 to 1,800; 1,800 to 2,000 Marketing€980k of €800kChart data Metric Value Target Marketing 980 600 Ranges: 0 to 600; 600 to 720; 720 to 800 Sales€610k of €900kChart data Metric Value Target Sales 610 675 Ranges: 0 to 675; 675 to 810; 810 to 900 Warehouse tiles, read bottom to top
A narrow dashboard tile has height, not width:
orientation="vertical"keeps the same value, target and bands, read bottom to top.labelnames the metric for the tooltip and the data table; the caption on the tile is the page’s own, because the page knows where the tile stands.Warehouse capacityPallet places in use · target 80 % Hamburg81% full812 of 1000Chart data Metric Value Target Hamburg 812 800 Ranges: 0 to 850; 850 to 950; 950 to 1,000 Bremen71% full640 of 900Chart data Metric Value Target Bremen 640 720 Ranges: 0 to 765; 765 to 855; 855 to 900 Rotterdam98% full1170 of 1200Chart data Metric Value Target Rotterdam 1,170 960 Ranges: 0 to 1,020; 1,020 to 1,140; 1,140 to 1,200
API generated from the package · cannot drift
Props
| Prop | Type | default | Description |
|---|---|---|---|
height | number | undefined | undefined | The canvas height in pixels; 48 by default. The data, the loading state and the empty state all take up the same height. |
width | number | undefined | 640 | Declared width: the first render goes from it, after that the width is measured. |
valueFormat | GrChartNumberFormat | undefined | undefined | The value format in the tooltip, the hidden table and announcements: decimal places and separators. |
tooltip | boolean | undefined | undefined | The panel with the values under the cursor; `true` by default. `false` hides only the panel: the cursor, the active marks, `update:activeIndex` and `pointHover` keep working while the chart is interactive. |
loading | boolean | undefined | false | Loading: a skeleton of the same height stands in place of the plot area, and the space for the axes is already reserved. |
empty | boolean | undefined | undefined | The empty state, set explicitly. Unset — the chart is empty when there is nothing to draw. |
emptyText | string | undefined | undefined | The placeholder text of an empty chart. Unset — "No data" from the package translations. |
dataTable | "hidden" | "visible" | "off" | undefined | undefined | The data table: `hidden` (the default) — for the screen reader only, `visible` — under the chart, `off` — no table. |
interactive | boolean | undefined | true | `false` — the chart becomes a picture: no focus, no tooltip, no keyboard. |
size | "md" | "xs" | "sm" | "lg" | undefined | undefined | The type size of the labels on the core control scale; by default — the size from `GrConfigProvider`. |
locale | string | undefined | undefined | The BCP-47 locale for numbers and dates. Unset — the language of the application translations, otherwise `en`. |
ariaLabel | string | undefined | undefined | The accessible name of the chart. Unset — a summary built from `label`, the measure and the target. |
ariaDescription | string | undefined | undefined | A description for the screen reader — the conclusion the chart is shown for; it adds to the summary rather than replacing it. |
dataTableMaxRows | number | "auto" | undefined | undefined | The row ceiling of the hidden data table. `'auto'` (the default) — as many rows as can be read; above the ceiling the table goes by an even sample and says so with a note, while point-by-point completeness stays with the keyboard. A number sets its own ceiling, `Infinity` removes it altogether, `dataTable: 'off'` removes the table entirely — which of these is needed is up to the application. |
orientation | "vertical" | "horizontal" | undefined | undefined | The direction of the track: `horizontal` (the default) or `vertical`. |
target | number | undefined | undefined | The target value — a tick across the track. |
ranges | readonly number[] | undefined | undefined | Bounds of the qualitative ranges: `[0.9, 1.0]` with `max: 1.2` gives three bands — up to 0.9, 0.9…1.0 and above 1.0. The order does not matter. |
max | number | undefined | undefined | Top of the scale. Not set — the maximum of the value, the target and the bounds, with headroom. |
min | number | undefined | undefined | The bottom of the scale; 0 by default. A bullet bar that does not start at zero distorts the length — and the value is read from the length. |
rangeColors | readonly string[] | undefined | undefined | Band colors from "good" to "bad". One longer than `ranges`. |
color | string | undefined | undefined | The tone of the value bar itself. |
label | string | undefined | undefined | Metric name: the row title in the table and in the tooltip. |
valuerequired | number | null | — | The measured value. `null` — there is no value; this is not a zero. |
Slots
| Slot | Type | Description |
|---|---|---|
tooltip | { active: GrChartActivePoint; formatValue: (value: number | null) => string; } | Custom content for the tooltip panel instead of the standard one; the data of the active position comes in the slot props. |
empty | any | A custom placeholder for an empty chart instead of "No data"; it takes up the same `height`. |
header | any | A header above the chart, inside its root — above a legend placed on top. |
Events
| Event | Signature | Description |
|---|---|---|
valueClick | (event: "valueClick", value: number | null): void | — |
Methods
| Methods | Signature | Description |
|---|---|---|
element | HTMLElement | null | The root element — for measurements and scrolling in the consumer. |
Types
Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.
import type { GrChartBulletConfigurableProps, GrChartSize, GrChartBulletProps } from '@feugene/granularity-charts/components/GrChartBullet'Filedist/types/components/GrChartBullet/config.d.ts
const grChartBulletConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartBullet">;Filedist/types/components/GrChartBullet/defaults.d.ts
interface GrChartBulletConfigurableProps {
size: GrChartSize;
height: number;
orientation: 'horizontal' | 'vertical';
tooltip: boolean;
dataTable: 'hidden' | 'visible' | 'off';
dataTableMaxRows: number | 'auto';
}Filedist/types/components/GrChartFrame/chartFrameStyles.d.ts
type GrChartSize = GrComponentSize;Filedist/types/components/GrChartBullet/safelist.d.ts
const grChartBulletSafelist: string[];Filedist/types/components/GrChartBullet/GrChartBullet.vue.d.ts
interface GrChartBulletProps {
value: number | null;
target?: number;
ranges?: readonly number[];
max?: number;
min?: number;
rangeColors?: readonly string[];
color?: string;
orientation?: 'horizontal' | 'vertical';
label?: string;
height?: number;
width?: number;
valueFormat?: GrChartNumberFormat;
tooltip?: boolean;
loading?: boolean;
empty?: boolean;
emptyText?: string;
dataTable?: 'hidden' | 'visible' | 'off';
interactive?: boolean;
size?: GrChartSize;
locale?: string;
ariaLabel?: string;
ariaDescription?: string;
dataTableMaxRows?: number | 'auto';
}interface GrChartBulletEmits {
(e: 'valueClick', value: number | null): void;
}Usage from the library prose
There is deliberately no dial in the package
A gauge spends a lot of room on little data and reads poorly quantitatively: from a needle one takes “roughly in the middle” rather than a number. A bullet solves the same task with a bar, and when there are several such metrics it is also comparable vertically, which dials cannot do at all.
Three weights that do not argue
The ranges are the background, the value a narrow bar on top, the target a notch across. The
thickness of the value bar is set by the --gr-chart-bullet-value-width token rather than by the
geometry: the bar is drawn with a <line>, and the theme changes its weight without touching the
track.
Colours of your own for the ranges are set by rangeColors — one per band, from “good” to “bad”.
There is always one band more than there are boundaries, even if a boundary went off the scale: such
a boundary is clamped rather than thrown away, otherwise the colours would move onto the
neighbouring bands.
value: null is not a zero
If there is no quantity, the bar is not drawn, the notch of the target remains and there is a dash in the table. For a cost that is the literal case: no write-offs means no cost, and a zero would lie here.
Together with the value the meter role disappears as well: it requires aria-valuenow, and without
a quantity there is none — a role left in place would give a violation of serious level.
A value beyond the scale is not truncated silently
The bar runs into the edge and gets an overflow marker, and the real quantity goes into the tooltip, into the hidden table and into the announcement. Truncating it quietly would mean showing a different number.
The role is meter rather than progressbar
progressbar is about the completion of a task that comes to an end; here there is a quantity that
simply is. slider fits even less: the value is not editable. Besides
aria-valuenow/valuemin/valuemax the overlay carries an aria-valuetext with a human wording
(“0.031 of 0.05, target 0.04”) — one number without units and without the target would say less than
a sighted person sees. The ranges are announced in the description of the chart: the coloured zones
would otherwise exist only for the sighted.
Limits
There are no several values in one track: a bullet compares one quantity with one target. The component does not draw a needle or the “gathering” of the value with an animation either.
Props in this example