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.
import { Sum, Difference, Product, Quotient, Ref } from "@umriss-ui/calculation";
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). 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.
format="percent" shows a ratio as per cent while it stays a ratio in every operation; a target is missed, never violated.
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>
);
}
Difference takes every operand after the first away from it, each with its minus in the operator column.
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>
);
}
A unit is a label you write, never converted or checked; decimals fixes the places shown while every operation uses the full value.
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>
);
}
Give a quantity an id where it first belongs and write Ref wherever it is used again; hover either place and both light up.
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>
);
}
.map inside an operator works; folded, a line with more than four operands says how many it holds instead of a long formula.
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>
);
}
limits are assessed through core's assess(); the verdict stands as a word beneath the number, never as a colour alone.
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>
);
}
The keys of Calculation apply here.
OperatorProps| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The operands, in order: quantities, givens and references. Shown in: Show a share against a target, Take several amounts away, Multiply quantities with units and 42 more |
labelrequired | string | — | What the quantity is called. It stands on its line, wherever it is an
operand, and in the accessible sentence. from QuantityPropsShown in: Show a share against a target, Take several amounts away, Multiply quantities 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 QuantityPropsShown in: Use a quantity twice, The availability of a service (Installation), Write a tree (Calculation) 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 QuantityPropsShown in: Show a share against a target, Take several amounts away, Multiply quantities 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 QuantityPropsShown in: Show a share against a target, Use a quantity twice, Build operands 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 QuantityPropsShown in: Multiply quantities with units, Build operands from data, Hold a figure to its limits and 29 more |
target | number | — | The value this quantity is meant to reach - missed, never violated. from QuantityPropsShown in: Show a share against a target, The availability of a service (Installation), Write a tree (Calculation) and 6 more |
limits | readonly Limit[] | — | Bounds the quantity is assessed against, through core's assess(). from QuantityPropsShown in: Hold a figure to its limits, Density (Calculation), Hold an interim to its limits (Chain) and 5 more |
explanation | string | — | A sentence of plain text beside the line: why the quantity is here, or
what it means. from QuantityPropsShown in: Say where a number came from (Given), A number one metric does not have (Metrics), Staff movement as a chain (Metrics) 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 QuantityPropsShown in: Say where a number came from (Given) |
emphasis | "strong" | "muted" | — | How much the line stands out among the others (ADR-0049): 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 QuantityPropsShown in: A stronger line (Calculation), A quieter line (Calculation), Emphasis inside a group (Calculation) |
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 QuantityPropsShown in: A rule above (Calculation), Emphasis inside a group (Calculation) |
RefProps| Name | Type | Default | Description |
|---|---|---|---|
torequired | string | — | The id of the quantity this reference stands for.Shown in: Use a quantity twice, The availability of a service (Installation), Write a tree (Calculation) and 10 more |
LimitFrom @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.
| Name | Type | Default | Description |
|---|---|---|---|
valuerequired | number | — | Shown in: Hold a figure to its limits, Density (Calculation), Hold an interim to its limits (Chain) and 3 more |
siderequired | Side"upper" | "lower" | — | Shown in: Hold a figure to its limits, Density (Calculation), Hold an interim to its limits (Chain) and 3 more |
severityrequired | Severity"warning" | "alarm" | — | Shown in: Hold a figure to its limits, Density (Calculation), Hold an interim to its limits (Chain) and 3 more |
SideFrom @umriss-ui/core.
Whether a limit is an upper or a lower bound.
type Side = "upper" | "lower";SeverityFrom @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.
type Severity = "warning" | "alarm";What umriss deliberately does not build, and why: ADR-0032.