Skip to main content

ReadingValue

Shows the latest value of a Reading as an easy-to-read number, like 22.4. It's the number people look at first, in a reading card, a Space overview or a detail view.

Loading…
Live editor
<ReadingValue value={22.4} />
Try it

The examples on this page are live. Change the code and the result updates as you type.

Import​

import { ReadingValue } from "@detriz-sh-kit/ui";

The component needs the kit's styles. Load them once, at the root of your app:

import "@detriz-sh-kit/tokens/tokens.css";
import "@detriz-sh-kit/ui/styles.css";

See Installation for how to install the packages.

Basic usage​

Pass the value as a number:

import { ReadingValue } from "@detriz-sh-kit/ui";

<ReadingValue value={22.4} />;

The examples below leave out the import line. In your app, you need it.

When to use it​

Use ReadingValue to show one value, the latest one, of one Metric.

Don't use it for:

  • A full Reading. To show the Metric name, the value and the Unit together, use SensorReading. It already includes ReadingValue.
  • The Unit ("°C"). Use UnitLabel next to it.
  • Connection Status as a label ("Stale", "Disconnected"). Use DeviceConnectionStatus. ReadingValue only reacts to Disconnected.
  • Several values or a trend. ReadingValue shows one value. Comparison and History components are planned for that.

States​

ReadingValue has three states. There is no state prop: the component picks the state from value and isDisconnected.

StateWhenShowsScreen readers say
Connectedvalue is a numberThe numberthe number
Loadingvalue is null: the first Reading hasn't arrived yetA spinning loading icon, no number"Loading"
DisconnectedisDisconnected is trueA disconnected icon, no number"Disconnected"

If both apply, Disconnected wins: with isDisconnected, the icon shows even when you pass a value. So the component never shows two states at once.

All three states have the same height, so nothing moves on the screen when the state changes.

Connected​

Loading…
Live editor
<ReadingValue value={22.4} />

Loading​

Loading…
Live editor
<ReadingValue value={null} />

Disconnected​

Loading…
Live editor
<ReadingValue value={22.4} isDisconnected />

The last value is hidden on purpose: a number from a Device that can't be reached would look up to date when it isn't.

Sizes​

Use the size prop. The default is lg. The sizes have the same names as UnitLabel's, so you can pass one size to both.

sizeNumberIconUse it for
lg48px32pxHero spot, detail view
md32px24pxReading card
sm20px20pxList rows, compact cards
xs14px16pxTables, inline in a sentence
Loading…
Live editor
<StackLayout>
  <ReadingValue value={22.4} size="lg" />
  <ReadingValue value={22.4} size="md" />
  <ReadingValue value={22.4} size="sm" />
  <ReadingValue value={22.4} size="xs" />
</StackLayout>

Formatting numbers​

You pass a number, not a string. ReadingValue formats it for you:

  • Decimal places come from fractionDigits. The default is 1.
  • The decimal mark and thousands separator follow the browser's language. In English 1250 shows as "1,250", in Polish as "1250". 22.4 shows as "22,4" in Polish.
  • Every digit is the same width, so the layout doesn't jump when 19.9 becomes 20.0.

Set fractionDigits for each Metric, so each one shows its natural precision:

MetricfractionDigitsExample valueShows (English)
Temperature1 (default)22.422.4
Temperature1-3.5-3.5
Humidity04848
CO₂012501,250
Illuminance0320320
Loading…
Live editor
<StackLayout>
  <ReadingValue value={22.4} size="md" />
  <ReadingValue value={-3.5} size="md" />
  <ReadingValue value={48} fractionDigits={0} size="md" />
  <ReadingValue value={1250} fractionDigits={0} size="md" />
</StackLayout>

The examples above use your browser's language, so they may look different from the table.

Live values​

When new Readings arrive, pass the new value. Watch the digits: they stay in place while the number changes.

Loading…
Live editor
function LiveTemperature() {
  const [value, setValue] = useState(19.6);

  useEffect(() => {
    // A new Reading every second, for the example.
    const timer = setInterval(() => {
      setValue((v) => Math.round((v + 0.1) * 10) / 10);
    }, 1000);
    return () => clearInterval(timer);
  }, []);

  return <ReadingValue value={value} />;
}

Pairing with UnitLabel​

ReadingValue shows the number only. Put a UnitLabel next to it, inside a parent that lines them up on the same baseline, with a 4px gap and the same size:

Loading…
Live editor
<span
  style={{
    display: "inline-flex",
    alignItems: "baseline",
    gap: "var(--shk-space-1)",
  }}
>
  <ReadingValue value={1250} fractionDigits={0} size="lg" />
  <UnitLabel unit="ppm" size="lg" />
</span>

The Unit stays visible in the Loading and Disconnected states, so people still know what was measured. See Pairing with ReadingValue on the UnitLabel page for the details, or use SensorReading, which does this for you.

Mapping your data​

PropComes fromExample
valueThe value of the latest Reading, or null before the first one22.4
isDisconnectedThe Device's Connection Status is DisconnectedconnectionStatus === "disconnected"
fractionDigitsThe Metric1 for Temperature, 0 for CO₂

A few rules from the domain model:

  • Stale: the Device is still Connected but late, so keep passing the last value. Show that it is late with DeviceConnectionStatus next to it.
  • Connecting: the Device is getting its connection back. You decide: pass the last value to keep showing it, or null to show Loading.
  • Fault: ReadingValue doesn't change for a Fault. Show it with DeviceFaultStatus.
  • Not paired: a Device that isn't Paired sends no Readings, so there is nothing to show. Don't render ReadingValue for it.

See Device lifecycle for how a Device moves between these statuses.

Props​

PropTypeDefaultDescription
valuenumber | null–Required. The latest value of the Reading. null while it is loading.
isDisconnectedbooleanfalseTrue when the Device's Connection Status is Disconnected. Hides the value and shows the disconnected icon.
fractionDigitsnumber1How many decimal places to show. Set it per Metric (Temperature 1, CO₂ 0).
size"xs" | "sm" | "md" | "lg""lg"How big the number is drawn.

The types ReadingValueProps and ReadingValueSize are exported too:

import type { ReadingValueProps, ReadingValueSize } from "@detriz-sh-kit/ui";

Long numbers​

A number is never wrapped or shortened: a cut-off number would show the wrong value. If its container is too narrow, the number sticks out of it, so leave enough room for the largest value you expect.

Loading…
Live editor
<div
  style={{ maxWidth: 120, outline: "1px dashed var(--shk-palette-gray-300)" }}
>
  <ReadingValue value={100000} fractionDigits={0} size="lg" />
</div>

Accessibility​

What you get without doing anything:

  • The Loading and Disconnected icons have labels, so screen readers say "Loading" or "Disconnected" instead of nothing.
  • Disconnected is told apart by its own icon shape, not by colour.
  • The loading icon stops spinning when the user has asked for reduced motion. It still shows, so loading is still visible.
  • The number and the icons meet the WCAG AA contrast ratio (4.5:1) on light backgrounds.

What's up to you:

  • Give the value its context. "22.4" on its own means little to a screen reader. Show it together with the Metric name and the Unit, so the group reads as "Temperature 22.4 degrees Celsius". SensorReading does this for you.
  • Announcing changes. ReadingValue has no live region, because Readings can arrive every few seconds and would keep interrupting. If a screen needs to announce a change, decide in your app how often, and don't put aria-live on a value that updates constantly.
  • Check the contrast if you place it on a dark or coloured background.

Styling​

ReadingValue uses the kit's design tokens (CSS variables starting with --shk-):

WhatToken
Number colour--shk-color-text-primary
Icon colour--shk-color-text-secondary
Number text stylelg readout-lg, md readout, sm readout-sm, xs readout-xs (--shk-typography-readout…)
Loading spin speed--shk-motion-duration-spin (1 second per turn)

ReadingValue has no padding or background: the space around it belongs to the parent.

To change how it looks, set a token on a parent element. The change applies to everything inside that element, not only ReadingValue:

.my-reading {
--shk-color-text-primary: var(--shk-palette-gray-800);
}