# Tree

> Package `@umriss-ui/calculation`, version 0.40.0. Demo page: <https://romanhaendler.github.io/umriss-ui/calculation/tree/>. Every page in one line: [llms.txt](https://romanhaendler.github.io/umriss-ui/calculation/llms.txt); every page in full: [llms-full.txt](https://romanhaendler.github.io/umriss-ui/calculation/llms-full.txt).

Sum, difference, product and quotient, each holding its operands: the form for a figure put together from factors, folded to its formula until a reader opens it.

```ts
import { Sum, Difference, Product, Quotient, Ref } from "@umriss-ui/calculation";
```

Demo page: https://romanhaendler.github.io/umriss-ui/calculation/tree/

`Product` and `Difference` (a − b − c) take two or more operands, `Quotient` exactly two, and `Sum` any number – rows from data, one or none included (see [Rows from data](https://romanhaendler.github.io/umriss-ui/calculation/rows-from-data/)). A quantity used twice is defined once with an `id` and stands elsewhere as a `Ref`, which shows its name and number but never its derivation again.

## Examples

### Show a share against a target

`format="percent"` shows a ratio as per cent while it stays a ratio in every operation; a `target` is missed, never violated.

```tsx
import { Calculation, Given, Quotient } from "@umriss-ui/calculation";

export default function ShareWithTarget() {
  return (
    <Calculation aria-label="Utilisation of the Web team, week 12">
      <Quotient label="Utilisation, Web team" format="percent" target={0.85}>
        <Given label="Hours booked on projects" value={141} unit="h" />
        <Given label="Hours the team can be planned for" value={176} unit="h" />
      </Quotient>
    </Calculation>
  );
}
```

### Take several amounts away

`Difference` takes every operand after the first away from it, each with its minus in the operator column.

```tsx
import { Calculation, Difference, Given } from "@umriss-ui/calculation";

export default function DifferenceOfThree() {
  return (
    <Calculation aria-label="Plannable hours, Chloe Durand, week 12">
      <Difference label="Plannable hours, Chloe" unit="h">
        <Given label="Capacity" value={40} unit="h" />
        <Given label="Leave" value={8} unit="h" />
        <Given label="Meetings" value={5} unit="h" />
        <Given label="Support rota" value={4} unit="h" />
      </Difference>
    </Calculation>
  );
}
```

### Multiply quantities with units

A `unit` is a label you write, never converted or checked; `decimals` fixes the places shown while every operation uses the full value.

```tsx
import { Calculation, Given, Product } from "@umriss-ui/calculation";

export default function ProductWithUnits() {
  return (
    <Calculation aria-label="Diesel cost of tour T-01">
      <Product label="Diesel, tour T-01" unit="€" decimals={2}>
        <Given label="Distance" value={87} unit="km" />
        <Given label="Consumption" value={0.095} unit="l/km" />
        <Given label="Diesel price" value={1.68} unit="€/l" decimals={2} />
      </Product>
    </Calculation>
  );
}
```

### Use a quantity twice

Give a quantity an `id` where it first belongs and write `Ref` wherever it is used again; hover either place and both light up.

```tsx
import { Calculation, Difference, Given, Quotient, Ref } from "@umriss-ui/calculation";

export default function UsedTwice() {
  return (
    <Calculation aria-label="Overspend of Marketing, February">
      <Quotient label="Overspend, share of budget" format="percent">
        <Difference label="Overspend" unit="€">
          <Given label="Actual, February" value={77420} unit="€" />
          <Given id="budget" label="Budget, February" value={68000} unit="€" />
        </Difference>
        <Ref to="budget" />
      </Quotient>
    </Calculation>
  );
}
```

### Build operands from data

`.map` inside an operator works; folded, a line with more than four operands says how many it holds instead of a long formula.

```tsx
import { Calculation, Difference, Given, Product, Quotient } from "@umriss-ui/calculation";

/* Data from the operations world, written out here so the example runs on its own. */

const at = (day: number, hours: number, minutes = 0) => new Date(2026, 2, day, hours, minutes).getTime();
const MINUTE = 60_000;

/** Tuesday, 17 March 2026, 10:30 - the moment the screens are read at. */
const NOW = at(17, 10, 30);

interface Service {
  id: string;
  name: string;
  team: string;
  /** 1 is customer-facing and pages at night; 3 waits for the morning. */
  tier: 1 | 2 | 3;
  /** The latency objective: the 95th percentile stays below this, in ms. */
  latencySlo: number;
  /** The availability promised for a month, in per cent. */
  availabilityTarget: number;
}

const SERVICES: readonly Service[] = [
  { id: "checkout", name: "Checkout", team: "Payments", tier: 1, latencySlo: 300, availabilityTarget: 99.95 },
  { id: "billing", name: "Billing", team: "Payments", tier: 1, latencySlo: 400, availabilityTarget: 99.9 },
  { id: "sign-in", name: "Sign-in", team: "Identity", tier: 1, latencySlo: 200, availabilityTarget: 99.95 },
  { id: "search", name: "Search", team: "Discovery", tier: 1, latencySlo: 250, availabilityTarget: 99.9 },
  { id: "images", name: "Image service", team: "Discovery", tier: 2, latencySlo: 500, availabilityTarget: 99.5 },
  { id: "notifications", name: "Notifications", team: "Messaging", tier: 2, latencySlo: 800, availabilityTarget: 99.5 },
  { id: "webhooks", name: "Webhooks", team: "Integrations", tier: 2, latencySlo: 1000, availabilityTarget: 99.5 },
  { id: "reports", name: "Reporting", team: "Insights", tier: 3, latencySlo: 2000, availabilityTarget: 99 },
];

/** The month so far: 1 March 00:00 to now, in minutes. */
const MONTH_MINUTES = (NOW - at(1, 0)) / MINUTE;

/** Minutes each service was down this month. With `MONTH_MINUTES` and the
    service's target, what an availability is worked out from. */
const DOWNTIME_MINUTES: Readonly<Record<string, number>> = {
  checkout: 34,
  billing: 18,
  "sign-in": 41,
  search: 85,
  images: 120,
  notifications: 12,
  webhooks: 210,
  reports: 95,
};

/* The services a booking passes through, in order. */
const PATH = ["sign-in", "search", "checkout", "billing", "notifications"];

export default function FromData() {
  return (
    <Calculation aria-label="Availability of a booking, March so far">
      <Product label="Availability of a booking" format="percent" decimals={2}>
        {SERVICES.filter((service) => PATH.includes(service.id)).map((service) => (
          <Quotient key={service.id} label={`Availability, ${service.name}`} format="percent" decimals={2}>
            <Difference label={`Minutes up, ${service.name}`} unit="min">
              <Given label="Minutes this month" value={MONTH_MINUTES} unit="min" />
              <Given label={`Minutes down, ${service.name}`} value={DOWNTIME_MINUTES[service.id]} unit="min" />
            </Difference>
            <Given label="Minutes this month" value={MONTH_MINUTES} unit="min" />
          </Quotient>
        ))}
      </Product>
    </Calculation>
  );
}
```

### Hold a figure to its limits

`limits` are assessed through core's `assess()`; the verdict stands as a word beneath the number, never as a colour alone.

```tsx
import { Calculation, Given, Quotient, Sum } from "@umriss-ui/calculation";

export default function Limits() {
  return (
    <Calculation aria-label="Cost per stop, tour T-03">
      <Quotient
        label="Cost per stop, tour T-03"
        unit="€/stop"
        decimals={2}
        limits={[
          { value: 60, side: "upper", severity: "warning" },
          { value: 100, side: "upper", severity: "alarm" },
        ]}
      >
        <Sum label="Cost of tour T-03" unit="€" decimals={2}>
          <Given label="Driver" value={216.2} unit="€" decimals={2} />
          <Given label="Diesel" value={71.37} unit="€" decimals={2} />
          <Given label="Truck, one day" value={145} unit="€" decimals={2} />
        </Sum>
        <Given label="Stops" value={5} unit="stops" />
      </Quotient>
    </Calculation>
  );
}
```

## When to use something else

- A sheet read top to bottom, line by line → Chain

## Keyboard

The keys of [Calculation](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#keyboard-calculation) apply here.

## API

### `OperatorProps`

| Prop | Type | Default | Description |
|---|---|---|---|
| `children` | `ReactNode` | — | The operands, in order: quantities, givens and references.<br>Shown in: [Show a share against a target](https://romanhaendler.github.io/umriss-ui/calculation/tree/#share-with-target), [Take several amounts away](https://romanhaendler.github.io/umriss-ui/calculation/tree/#difference-of-three), [Multiply quantities with units](https://romanhaendler.github.io/umriss-ui/calculation/tree/#product-with-units) and 42 more |
| `label` *required* | `string` | — | What the quantity is called. It stands on its line, wherever it is an operand, and in the accessible sentence. From `QuantityProps`.<br>Shown in: [Show a share against a target](https://romanhaendler.github.io/umriss-ui/calculation/tree/#share-with-target), [Take several amounts away](https://romanhaendler.github.io/umriss-ui/calculation/tree/#difference-of-three), [Multiply quantities with units](https://romanhaendler.github.io/umriss-ui/calculation/tree/#product-with-units) and 50 more |
| `id` | `string` | — | Makes the quantity referable: `<Ref to>` stands for it elsewhere. Needed only for a quantity used more than once. From `QuantityProps`.<br>Shown in: [Use a quantity twice](https://romanhaendler.github.io/umriss-ui/calculation/tree/#used-twice), [The availability of a service (Installation)](https://romanhaendler.github.io/umriss-ui/calculation/installation/#availability), [Write a tree (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#oee) and 10 more |
| `unit` | `string` | — | A label written after the number - "min", "pcs", "€". Never converted and never checked; the calculation does no unit algebra. From `QuantityProps`.<br>Shown in: [Show a share against a target](https://romanhaendler.github.io/umriss-ui/calculation/tree/#share-with-target), [Take several amounts away](https://romanhaendler.github.io/umriss-ui/calculation/tree/#difference-of-three), [Multiply quantities with units](https://romanhaendler.github.io/umriss-ui/calculation/tree/#product-with-units) and 42 more |
| `format` | `"percent"` | — | `percent` presents a plain ratio as per cent: 0.916 is shown as 91.6 %. The quantity stays a ratio in every operation. From `QuantityProps`.<br>Shown in: [Show a share against a target](https://romanhaendler.github.io/umriss-ui/calculation/tree/#share-with-target), [Use a quantity twice](https://romanhaendler.github.io/umriss-ui/calculation/tree/#used-twice), [Build operands from data](https://romanhaendler.github.io/umriss-ui/calculation/tree/#from-data) and 17 more |
| `decimals` | `number` | one digit for a percentage, at most two for a derived number, a given as it was given | Fixed number of fraction digits as shown. Rounding happens only in presentation; every operation uses the full value. From `QuantityProps`.<br>Shown in: [Multiply quantities with units](https://romanhaendler.github.io/umriss-ui/calculation/tree/#product-with-units), [Build operands from data](https://romanhaendler.github.io/umriss-ui/calculation/tree/#from-data), [Hold a figure to its limits](https://romanhaendler.github.io/umriss-ui/calculation/tree/#limits) and 29 more |
| `target` | `number` | — | The value this quantity is meant to reach - missed, never violated. From `QuantityProps`.<br>Shown in: [Show a share against a target](https://romanhaendler.github.io/umriss-ui/calculation/tree/#share-with-target), [The availability of a service (Installation)](https://romanhaendler.github.io/umriss-ui/calculation/installation/#availability), [Write a tree (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#oee) and 6 more |
| `limits` | `readonly `[`Limit`](https://romanhaendler.github.io/umriss-ui/calculation/tree/#type-Limit)`[]` | — | Bounds the quantity is assessed against, through core's `assess()`. From `QuantityProps`.<br>Shown in: [Hold a figure to its limits](https://romanhaendler.github.io/umriss-ui/calculation/tree/#limits), [Density (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#density), [Hold an interim to its limits (Chain)](https://romanhaendler.github.io/umriss-ui/calculation/chain/#error-budget) and 5 more |
| `explanation` | `string` | — | A sentence of plain text beside the line: why the quantity is here, or what it means. From `QuantityProps`.<br>Shown in: [Say where a number came from (Given)](https://romanhaendler.github.io/umriss-ui/calculation/given/#where-it-came-from), [A number one metric does not have (Metrics)](https://romanhaendler.github.io/umriss-ui/calculation/metrics/#not-recorded), [Staff movement as a chain (Metrics)](https://romanhaendler.github.io/umriss-ui/calculation/metrics/#staff-movement) and 2 more |
| `aside` | `ReactNode` | — | Content of the caller's own beside the line - a sparkline, a link. It never takes part in the calculation. From `QuantityProps`.<br>Shown in: [Say where a number came from (Given)](https://romanhaendler.github.io/umriss-ui/calculation/given/#where-it-came-from) |
| `emphasis` | `"strong" \| "muted"` | — | How much the line stands out among the others ([ADR-0049](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0049-a-sum-takes-rows-from-data.md)): `strong` to be read first, `muted` to recede - a muted line still counts in full. Not on the Result, which is already the heaviest line. From `QuantityProps`.<br>Shown in: [A stronger line (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#a-stronger-line), [A quieter line (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#a-quieter-line), [Emphasis inside a group (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#emphasis-inside-a-group) |
| `rule` | `"above"` | — | `above` rules the line off from the ones before it, where a section starts that is not an interim. Not on the Result. From `QuantityProps`.<br>Shown in: [A rule above (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#a-rule-above), [Emphasis inside a group (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#emphasis-inside-a-group) |

### `RefProps`

| Prop | Type | Default | Description |
|---|---|---|---|
| `to` *required* | `string` | — | The `id` of the quantity this reference stands for.<br>Shown in: [Use a quantity twice](https://romanhaendler.github.io/umriss-ui/calculation/tree/#used-twice), [The availability of a service (Installation)](https://romanhaendler.github.io/umriss-ui/calculation/installation/#availability), [Write a tree (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#oee) and 10 more |

### Types on this page

#### `Limit`

From `@umriss-ui/core`.

A bound: a value, a side, a severity.

No tolerance band as input. A band is derived - two limits of equal
severity on opposite sides enclose one - and as an input type it would
force a `null` on one edge in the one-sided case.

| Prop | Type | Default | Description |
|---|---|---|---|
| `value` *required* | `number` | — | <br>Shown in: [Hold a figure to its limits](https://romanhaendler.github.io/umriss-ui/calculation/tree/#limits), [Density (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#density), [Hold an interim to its limits (Chain)](https://romanhaendler.github.io/umriss-ui/calculation/chain/#error-budget) and 3 more |
| `side` *required* | [`Side`](https://romanhaendler.github.io/umriss-ui/calculation/tree/#type-Side)<br>`"upper" \| "lower"` | — | <br>Shown in: [Hold a figure to its limits](https://romanhaendler.github.io/umriss-ui/calculation/tree/#limits), [Density (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#density), [Hold an interim to its limits (Chain)](https://romanhaendler.github.io/umriss-ui/calculation/chain/#error-budget) and 3 more |
| `severity` *required* | [`Severity`](https://romanhaendler.github.io/umriss-ui/calculation/tree/#type-Severity)<br>`"warning" \| "alarm"` | — | <br>Shown in: [Hold a figure to its limits](https://romanhaendler.github.io/umriss-ui/calculation/tree/#limits), [Density (Calculation)](https://romanhaendler.github.io/umriss-ui/calculation/calculation/#density), [Hold an interim to its limits (Chain)](https://romanhaendler.github.io/umriss-ui/calculation/chain/#error-budget) and 3 more |

#### `Side`

From `@umriss-ui/core`.

Whether a limit is an upper or a lower bound.

```ts
type Side = "upper" | "lower";
```

#### `Severity`

From `@umriss-ui/core`.

How seriously a violation weighs: one severity that advises and one that
calls for action. Exactly two; for a third distinction one sets a
second limit.

```ts
type Severity = "warning" | "alarm";
```

## Known limits

- Four operators and nothing else – no powers, roots or functions; compute those before and hand them in as givens.
- A unit is a label and is never converted or checked; the calculation does no unit algebra.

What umriss deliberately does not build, and why: [ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/).
