Skip to content

Import#

import { Stat } from '@dnb/eufemia'

Description#

Stat contains components for prominent values with a label, where typography and visual emphasis are part of the component.

Available components#

  • Stat.Root renders a definition list (dl).

    • Stat.Label renders descriptive text with dedicated typography and color for metric context (dt).

    • Stat.Content renders the main value as a definition description (dd).

  • Stat.Number is the base value formatter built on the NumberFormat formatting logic.

  • Stat.Currency and Stat.Percent are convenience wrappers around Stat.Number.

    • It adds typography-specific properties such as fontSize, fontWeight and colorizeBySign, along with mainSize and auxiliarySize as well as mainWeight and auxiliaryWeight that can be used to customize the visual emphasis of the different parts of the value (currency symbol or percent sign).
  • Stat.Trend renders explicit plus (+) and minus (−) indicators with red/green background states and screen-reader text.

  • Stat.Rating renders a star rating (defaults to 5 stars) and colorizes stars based on value. The max prop is clamped to 20 to prevent excessive DOM output; a console warning is emitted when the limit is exceeded.

  • Stat.Info renders supporting text with a smaller, muted style.

  • Stat.Inline is a horizontal layout container for grouping content elements like Stat.Trend and Stat.Info side by side with consistent spacing and alignment.

  • Stat.Text renders custom content and supports properties such as fontSize, fontWeight, and colorizeBySign.

Accessibility#

  • Stat.Root provides semantic definition-list markup (dl), where Stat.Label is rendered as dt and Stat.Content as dd.

  • If the label also acts as a section heading, use a heading element inside Stat.Label (for example H3) to preserve a meaningful heading outline.

  • Use srLabel to prepend context in the screen-reader text only, for example turning 1,234 kr into Revenue 1,234 kr for screen readers.

  • When e.g. signDisplay="always" is used, the sign is rendered as a separate visual element with CSS spacing, while the accessible text stays based on the formatted number string.

  • Negative values follow NumberFormat's minus-sign conventions, including normalization to the Unicode minus sign (U+2212, −) for visual and screen-reader output.

  • Currency and Percent provide screen-reader text based on the formatted value. Percentages include the localized unit name, such as "prosent" in Norwegian.

  • Trend preserves the accessible content of composed children, including their locale, rounding, units and labels. A Trend srLabel is announced before the child content. Use Stat.Percent or NumberFormat.Percent inside Stat.Trend for localized percentages. Set decimals to the required precision and signDisplay="always" on the child formatter so direction is conveyed by a sign as well as color. Plain text and numeric Trend values are displayed as supplied without percentage formatting.

  • Rating uses an accessible label (role="img" + aria-label) that includes value and max.

Relevant links#

Related components#

Stat is part of the Content category. Other components for similar needs:

  • Accordion — to let people open and close sections of related content.
  • Avatar — to make a person, company, or profile easier to recognize.
  • Card — to group related content in a clear, separated area.
  • CountryFlag — to show a country by its flag from an ISO country code.
  • DateFormat — to show dates in the correct DNB format.
  • Heading — to create accessible page headings with the correct level.

See all in Content →

Demos#

Basic usage#

Revenue growth
Some additional information.
<Stat.Root>
  <Stat.Label>Revenue growth</Stat.Label>
  <Stat.Content direction="vertical">
    <Stat.Currency value={1234} signDisplay="always" />
    <Stat.Trend srLabel="Change">
      <Stat.Percent value={12.4} decimals={1} signDisplay="always" />
    </Stat.Trend>
    <Stat.Info>Some additional information.</Stat.Info>
  </Stat.Content>
</Stat.Root>

Root and Label#

If the label acts as a section heading, place a heading element inside Stat.Label (for example H3).

Revenue growth

Monthly change
(some additional information)
<Stat.Root>
  <Stat.Label>
    <H3>Revenue growth</H3>
  </Stat.Label>
  <Stat.Content direction="vertical">
    <Stat.Currency
      value={1234}
      mainSize="x-large"
      auxiliarySize="x-small"
    />
    <Stat.Trend srLabel="Growth trend">
      <Stat.Percent value={12.4} decimals={1} signDisplay="always" />
    </Stat.Trend>
  </Stat.Content>

  <Stat.Label top>Monthly change</Stat.Label>
  <Stat.Content direction="vertical">
    <Stat.Currency value={-1234} locale="en-GB" />
    <Stat.Inline>
      <Stat.Trend srLabel="Change trend">
        <Stat.Percent value={-2.1} decimals={1} signDisplay="always" />
      </Stat.Trend>
      <Stat.Info>(some additional information)</Stat.Info>
    </Stat.Inline>
  </Stat.Content>
</Stat.Root>

Hidden Label#

Use a visually hidden label (srOnly) when the visible UI context already describes the statistic.

I'm a hidden label
<Stat.Root>
  <Stat.Label srOnly>I'm a hidden label</Stat.Label>
  <Stat.Content>
    <Stat.Currency value={1234} />
  </Stat.Content>
</Stat.Root>

Currency#

You can use mainSize and auxiliarySize to adjust the relative size of the currency symbol and the amount.

Always show sign
With suffix
Colorized using en-GB locale
<Stat.Root>
  <Stat.Label>Always show sign</Stat.Label>
  <Stat.Content>
    <Stat.Currency
      value={1234}
      mainSize="x-large"
      signDisplay="always"
      auxiliarySize="x-small"
    />
  </Stat.Content>

  <Stat.Label top>With suffix</Stat.Label>
  <Stat.Content>
    <Stat.Currency
      value={1234}
      currency="USD"
      suffix="/mnd"
      mainSize="x-large"
      auxiliarySize="x-small"
    />
  </Stat.Content>

  <Stat.Label top>
    Colorized using <Code>en-GB</Code> locale
  </Stat.Label>
  <Stat.Content>
    <Stat.Currency
      value={-1234.5}
      decimals={2}
      currency="USD"
      signDisplay="always"
      fontSize="medium"
      colorizeBySign
      locale="en-GB"
    />
  </Stat.Content>
</Stat.Root>

Currency within a Trend#

()
<Stat.Root>
  <Stat.Label>
    <DateFormat value="P1Y" />
  </Stat.Label>
  <Stat.Content direction="vertical">
    <Stat.Currency value={350234} srLabel="Annual revenue" />
    <Stat.Inline>
      <Stat.Trend>
        <Stat.Currency
          value={46692}
          signDisplay="always"
          srLabel="Revenue delta"
        />
      </Stat.Trend>
      <Stat.Info>
        (
        <Stat.Percent
          value={16.79}
          decimals={2}
          srLabel="Relative change"
        />
        )
      </Stat.Info>
    </Stat.Inline>
  </Stat.Content>
</Stat.Root>

Percent within a Trend#

Compose a percentage formatter inside Stat.Trend to keep the displayed and accessible values aligned. Set decimals to the precision you need and signDisplay="always" on the child formatter to show the direction with a sign as well as color.

function Example() {
  const [value, setValue] = useState(-1.55)
  return (
    <>
      <Stat.Trend srLabel="Change">
        <Stat.Percent value={value} decimals={2} signDisplay="always" />
      </Stat.Trend>
      <Button
        text="Update change"
        left
        onClick={() => setValue(value < 0 ? 1.8422381750579178 : -1.55)}
      />
    </>
  )
}
render(<Example />)

Number#

Number
Number in Trend and Info
()
<Stat.Root>
  <Stat.Label>Number</Stat.Label>
  <Stat.Content>
    <Stat.Number
      value={1234}
      signDisplay="always"
      mainSize="x-large"
      auxiliarySize="x-small"
    />
  </Stat.Content>

  <Stat.Label top>Number in Trend and Info</Stat.Label>
  <Stat.Content>
    <Stat.Trend tone="negative" srLabel="Negative trend">
      <Stat.Number value={-1234} signDisplay="always" />
    </Stat.Trend>
    <Stat.Info>
      (
      <Stat.Number value={1234} srLabel="Signed amount with currency" />)
    </Stat.Info>
  </Stat.Content>
</Stat.Root>

Percent#

Percentage
Percentage colorized
<Stat.Root>
  <Stat.Label>Percentage</Stat.Label>
  <Stat.Content>
    <Stat.Percent
      value={12.3}
      mainSize="x-large"
      auxiliarySize="x-small"
    />
  </Stat.Content>

  <Stat.Label top>Percentage colorized</Stat.Label>
  <Stat.Content>
    <Stat.Percent
      value={0.1234}
      decimals={2}
      signDisplay="always"
      fontSize="medium"
      colorizeBySign
    />
  </Stat.Content>
</Stat.Root>

Percent colorized by sign#

Positive without signDisplay
Negative without signDisplay
Zero without signDisplay
<Stat.Root>
  <Stat.Label>Positive without signDisplay</Stat.Label>
  <Stat.Content>
    <Stat.Percent value={12.3} fontSize="medium" colorizeBySign />
  </Stat.Content>

  <Stat.Label top>Negative without signDisplay</Stat.Label>
  <Stat.Content>
    <Stat.Percent value={-12.3} fontSize="medium" colorizeBySign />
  </Stat.Content>

  <Stat.Label top>Zero without signDisplay</Stat.Label>
  <Stat.Content>
    <Stat.Percent value={0} fontSize="medium" colorizeBySign />
  </Stat.Content>
</Stat.Root>

Rating#

Stars rating
Progressive rating
<Stat.Root>
  <Stat.Label>Stars rating</Stat.Label>
  <Stat.Content>
    <Stat.Rating value={4} />
  </Stat.Content>

  <Stat.Label top>Progressive rating</Stat.Label>
  <Stat.Content direction="vertical">
    <Stat.Rating variant="progressive" value={5} />
  </Stat.Content>
</Stat.Root>

Text#

Label
Custom content
With medium font weight and size
Larger and bolder
<Stat.Root>
  <Stat.Label>Label</Stat.Label>
  <Stat.Content>
    <Stat.Text colorizeBySign={-123}>Custom content</Stat.Text>
  </Stat.Content>

  <Stat.Label top>With medium font weight and size</Stat.Label>
  <Stat.Content>
    <Stat.Text
      srLabel="Screen reader label"
      colorizeBySign={123}
      fontWeight="medium"
      fontSize="medium"
    >
      Larger and bolder
    </Stat.Text>
  </Stat.Content>
</Stat.Root>

With Subtle Label#

Also, the order of the content and label can be switched for visual users (not screen readers), and the label is styled with the subtle variant to further de-emphasize it.

function Example() {
  const { rating } = useTranslation().Stat
  return (
    <Grid.Container
      rowGap
      columnGap
      style={{
        gridAutoRows: '1fr',
      }}
    >
      <Grid.Item
        span={{
          small: [1, 12],
          medium: [1, 12],
          large: [1, 3],
        }}
      >
        <Card
          style={{
            height: '100%',
          }}
        >
          <Stat.Root visualOrder="content-label">
            <Stat.Label variant="subtle">
              <DateFormat value="P1Y" />
            </Stat.Label>
            <Stat.Content direction="vertical">
              <IconPrimary icon="arrow_up" top="x-small" />
              <Stat.Percent
                top="small"
                value={5.21}
                decimals={2}
                fontSize="basis"
                srLabel="Revenue growth percentage"
              />
            </Stat.Content>
          </Stat.Root>
        </Card>
      </Grid.Item>

      <Grid.Item
        span={{
          small: [1, 12],
          medium: [1, 12],
          large: [4, 6],
        }}
      >
        <Card
          style={{
            height: '100%',
          }}
        >
          <Stat.Root visualOrder="content-label">
            <Stat.Label variant="subtle">Yearly cost</Stat.Label>
            <Stat.Content direction="vertical">
              <Icon icon={globe_medium} />
              <Stat.Percent
                top="small"
                value={0.6}
                decimals={1}
                fontSize="basis"
              />
            </Stat.Content>
          </Stat.Root>
        </Card>
      </Grid.Item>

      <Grid.Item
        span={{
          small: [1, 12],
          medium: [1, 12],
          large: [7, 9],
        }}
      >
        <Card
          style={{
            height: '100%',
          }}
        >
          <Stat.Root visualOrder="content-label">
            <Stat.Label variant="subtle">Risiko</Stat.Label>
            <Stat.Content direction="vertical">
              <Stat.Rating variant="progressive" value={2} />
              <Stat.Info top variant="prominent">
                Lav
              </Stat.Info>
            </Stat.Content>
          </Stat.Root>
        </Card>
      </Grid.Item>

      <Grid.Item
        span={{
          small: [1, 12],
          medium: [1, 12],
          large: [10, 12],
        }}
      >
        <Card
          style={{
            height: '100%',
          }}
        >
          <Stat.Root visualOrder="content-label">
            <Stat.Label variant="subtle">Stars rating</Stat.Label>
            <Stat.Content direction="vertical">
              <Stat.Rating value={2} />
              <Stat.Info top variant="prominent">
                {rating.replace('%value', '2').replace('%max', '5')}
              </Stat.Info>
            </Stat.Content>
          </Stat.Root>
        </Card>
      </Grid.Item>
    </Grid.Container>
  )
}
render(<Example />)

With AriaLive#

Use the AriaLive component to announce dynamic value updates to screen readers. Wrap Stat.Root with AriaLive so that changes are announced when the content updates.

Revenue
function Example() {
  const [value, setValue] = useState(1234)
  return (
    <Flex.Stack>
      <AriaLive variant="content">
        <Stat.Root>
          <Stat.Label>Revenue</Stat.Label>
          <Stat.Content>
            <Stat.Currency value={value} />
          </Stat.Content>
        </Stat.Root>
      </AriaLive>

      <Button
        text="Update value"
        variant="secondary"
        onClick={() => setValue((prev) => prev + 100)}
      />
    </Flex.Stack>
  )
}
render(<Example />)
Suggest an edit