# UmrissProvider

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

Sets once, at the root, what an application shares: the density of its tables, where overlays open, how long toasts stay and where they stand, and the language. It is optional; every component works the same without it.

```ts
import { UmrissProvider } from "@umriss-ui/core";
```

Demo page: https://romanhaendler.github.io/umriss-ui/core/umrissprovider/

It holds these four settings and nothing else: no theme (see [Installation](https://romanhaendler.github.io/umriss-ui/core/installation/)) and no defaults for a component's own props. It writes nothing onto the document.

Keep the objects you pass outside the component or memoised: a new object on every render is a new setting on every render.

## Examples

### At the root

Wrap the application once; here `toast` keeps every toast on screen for eight seconds unless the toast names its own duration.

```tsx
import { Button, ToastProvider, UmrissProvider, useToast, type ToastConfig } from "@umriss-ui/core";

type ToastFn = ReturnType<typeof useToast>["toast"];

const deploy = (toast: ToastFn) => toast({ title: "Checkout 4.12.0 deployed", description: "Rolled out to all regions.", tone: "success" });

function DeployButton() {
  const { toast } = useToast();
  return (
    <Button variant="primary" onClick={() => deploy(toast)}>
      Deploy Checkout
    </Button>
  );
}

/* Kept outside the component: a new object on every render would be a new
   setting on every render. */
const TOAST: ToastConfig = { duration: 8000 };

export default function AtTheRoot() {
  return (
    <UmrissProvider toast={TOAST}>
      <ToastProvider>
        <DeployButton />
      </ToastProvider>
    </UmrissProvider>
  );
}
```

### Open overlays inside a full-screen panel

A full-screen element hides everything outside it, overlays included; point `portalTarget` at the element so menus and tooltips open inside it.

```tsx
import { useRef } from "react";
import { Button, Card, CardBody, CardHeader, Menu, MenuItem, Text, UmrissProvider } from "@umriss-ui/core";

export default function PortalTarget() {
  const panel = useRef<HTMLDivElement>(null);

  return (
    <div ref={panel} style={{ background: "var(--u-color-bg)" }}>
      {/* A function, because the element exists only after the first render. */}
      <UmrissProvider portalTarget={() => panel.current}>
        <Card>
          <CardHeader
            eyebrow="Sprint 14"
            title="Web team board"
            actions={
              <>
                <Button size="sm" onClick={() => void panel.current?.requestFullscreen?.()}>
                  Full screen
                </Button>
                <Menu trigger={<Button size="sm">Move card</Button>}>
                  <MenuItem onSelect={() => {}}>To review</MenuItem>
                  <MenuItem onSelect={() => {}}>To done</MenuItem>
                  <MenuItem onSelect={() => {}}>Back to the backlog</MenuItem>
                </Menu>
              </>
            }
          />
          <CardBody>
            <Text size="sm" tone="secondary">
              Checkout flow redesign · Chloe Durand · 5 points
            </Text>
          </CardBody>
        </Card>
      </UmrissProvider>
    </div>
  );
}
```

### Set up an application

All four settings, as a root sets them: German wording and notation, compact tables, overlays in the application's element, short toasts.

```tsx
import { useRef, useState } from "react";
import { Button, DatePicker, FormField, Stack, ToastProvider, UmrissProvider, useToast } from "@umriss-ui/core";
import type { ToastConfig, UmrissProviderProps } from "@umriss-ui/core";
import { GERMAN_FORMATS, GERMAN_WORDING } from "@umriss-ui/core/wording/de";

const LANGUAGE: UmrissProviderProps["language"] = { wording: GERMAN_WORDING, formats: GERMAN_FORMATS };
const TOAST: ToastConfig = { duration: 3000 };

type ToastFn = ReturnType<typeof useToast>["toast"];

const rebook = (toast: ToastFn) => toast({ title: "Shipment SH-1042 rebooked" });

function DeliveryDate() {
  const { toast } = useToast();
  const rebookNow = () => rebook(toast);
  const [date, setDate] = useState<Date | null>(new Date(2026, 2, 18));
  return (
    <Stack gap={3} align="flex-start">
      <FormField label="Delivery date">
        <DatePicker value={date} onChange={setDate} clearable />
      </FormField>
      <Button size="sm" disabled={date === null} onClick={rebookNow}>
        Rebook
      </Button>
    </Stack>
  );
}

export default function EverythingAtOnce() {
  const root = useRef<HTMLDivElement>(null);

  return (
    <div ref={root}>
      <UmrissProvider density="compact" portalTarget={() => root.current} toast={TOAST} language={LANGUAGE}>
        <ToastProvider>
          <DeliveryDate />
        </ToastProvider>
      </UmrissProvider>
    </div>
  );
}
```

## When to use something else

- Only the wording or the formats of one section differ → Language

## Keyboard

The keys of [Button](https://romanhaendler.github.io/umriss-ui/core/button/#keyboard-button), [Menu](https://romanhaendler.github.io/umriss-ui/core/menu/#keyboard-menu) and [DatePicker](https://romanhaendler.github.io/umriss-ui/core/datepicker/#keyboard-datepicker) apply here.

## Accessibility

Where a `ToastProvider` stands inside it, the toasts' two live regions stand where the overlays go: in the `portalTarget` before the first message, so that it is heard there, and inside the topmost modal dialog while one is open. [Toast](https://romanhaendler.github.io/umriss-ui/core/toast/) describes them.

## API

### `UmrissProviderProps`

| Prop | Type | Default | Description |
|---|---|---|---|
| `density` | [`Density`](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#type-Density)<br>`"comfortable" \| "compact"` | none - a component keeps its own density | The default for the `density` of every component that reads it with `useDensityFor` - in `@umriss-ui/table` `Table`, in `@umriss-ui/calculation` `Calculation` ("comfortable" means "regular").<br>Shown in: [Set up an application](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#everything-at-once) |
| `portalTarget` | [`PortalTarget`](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#type-PortalTarget) | `null` | Where overlays portal to, unless an open dialog takes them. A function is evaluated on every opening - a target that only enters the tree later cannot be given otherwise; `null` means "as before". The callers' own rules take precedence: the popover seam portals into the nearest `<dialog>` ancestor, because a portal at the body does not reach the top layer, and the toasts follow the topmost modal dialog, because a toast outside it lies behind it and is inert.<br>Shown in: [Open overlays inside a full-screen panel](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#portal-target), [Set up an application](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#everything-at-once) |
| `toast` | [`ToastConfig`](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#type-ToastConfig) | — | The setting for toasts: the display duration where the individual toast names none, where they stand, and how many stand at once.<br>Shown in: [At the root](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#at-the-root), [Set up an application](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#everything-at-once), [Where they stand (Toast)](https://romanhaendler.github.io/umriss-ui/core/toast/#where-they-stand) and 1 more |
| `language` | [`LanguageOptions`](https://romanhaendler.github.io/umriss-ui/core/language/#type-LanguageOptions) | none - the surrounding language holds, English at the root | Formats and wording, entry by entry - see [Language](https://romanhaendler.github.io/umriss-ui/core/language/).<br>Shown in: [Set up an application](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#everything-at-once) |
| `children` *required* | `ReactNode` | — | The subtree the settings apply to.<br>Shown in: [Open overlays inside a full-screen panel](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#portal-target), [Set up an application](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#everything-at-once) |

### `ToastConfig`

| Prop | Type | Default | Description |
|---|---|---|---|
| `duration` | `number` | `5000` | Display duration in ms, where the individual toast names none.<br>Shown in: [At the root](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#at-the-root), [Set up an application](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#everything-at-once) |
| `position` | [`ToastPosition`](https://romanhaendler.github.io/umriss-ui/core/umrissprovider/#type-ToastPosition)<br>`"top-start" \| "top-center" \| "top-end" \| "bottom-start" \| "bottom-center" \| "bottom-end"` | `"bottom-end"` | Where the toasts stand. There is no position per toast - toasts that stand in different places are not read as one.<br>Shown in: [Where they stand (Toast)](https://romanhaendler.github.io/umriss-ui/core/toast/#where-they-stand) |
| `limit` | `number` | `3` | How many toasts that leave by themselves stand at once; the oldest of them gives way to a new one, as if it had been closed. A toast that stays (`duration: 0`) or is still at work (`loading`) neither counts nor gives way.<br>Shown in: [How many stand at once (Toast)](https://romanhaendler.github.io/umriss-ui/core/toast/#how-many-stand) |

### Types on this page

#### `Density`

How densely the library builds. A component with a density of its own
takes it through `useDensityFor` as the default for its `density` - in

```ts
type Density = "comfortable" | "compact";
```

#### `PortalTarget`

Where overlays portal to.

A function is evaluated on every opening - a target that only comes into the
tree later cannot be given otherwise. `null`, or a result of `null`, means
"as before".

```ts
type PortalTarget = HTMLElement | (() => HTMLElement | null) | null;
```

#### `ToastPosition`

Where the toasts stand: an edge of the window and a place along it. On a
phone they take the width of the window, and only the edge counts.

```ts
type ToastPosition = "top-start" | "top-center" | "top-end" | "bottom-start" | "bottom-center" | "bottom-end";
```

## Known limits

- No theme and no default variants for components: a component is configured by its own props.

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