VisuallyHidden

Text a screen reader announces but the eye does not see (also called screen-reader-only or sr-only). Reach for it when a visible label is clear from its surroundings but not on its own, and for a skip link.

import { VisuallyHidden, announce } from "@umriss-ui/core";

Examples

Complete a label

The eye reads "Acknowledge" beside the incident; a screen reader hears which one.

import { Button, VisuallyHidden } from "@umriss-ui/core";

export default function CompleteALabel() {
  return (
    <Button size="sm">
      Acknowledge
      <VisuallyHidden> incident INC-1048</VisuallyHidden>
    </Button>
  );
}

Render it as a link with focusable: it is the first Tab stop on the page and shows itself only while it has focus.

import { Stack, Text, VisuallyHidden } from "@umriss-ui/core";

export default function SkipLink() {
  return (
    <Stack gap={2} align="flex-start">
      <VisuallyHidden as="a" href="#shipments" focusable>
        Skip to the shipment list
      </VisuallyHidden>
      <Text size="sm" tone="secondary">
        Tab into this example: the link appears above this line.
      </Text>
    </Stack>
  );
}

Name the same action in every row

Twenty "Edit budget" buttons sound alike to a screen reader; append each row's name hidden and each says what it edits.

import { Button, Stack, Text, VisuallyHidden } from "@umriss-ui/core";

/* Data from the controlling world, written out here so the example runs on its own. */
interface CostCentre {
  id: string;
  name: string;
  owner: string;
  /** The budget of one month. */
  monthlyBudget: number;
}

const COST_CENTRES: readonly CostCentre[] = [
  { id: "CC-1100", name: "Sales", owner: "Helen Marsh", monthlyBudget: 142_000 },
  { id: "CC-1200", name: "Marketing", owner: "Rafael Ortiz", monthlyBudget: 68_000 },
  { id: "CC-2100", name: "Engineering", owner: "Anika Sørensen", monthlyBudget: 188_000 },
  { id: "CC-2200", name: "Design", owner: "Paul Whitaker", monthlyBudget: 54_000 },
  { id: "CC-3100", name: "Customer service", owner: "Grace Obi", monthlyBudget: 61_000 },
  { id: "CC-4100", name: "Finance", owner: "Martina Vogel", monthlyBudget: 47_000 },
  { id: "CC-4200", name: "People", owner: "Daniel Frost", monthlyBudget: 39_000 },
  { id: "CC-4300", name: "IT", owner: "Kenji Arai", monthlyBudget: 83_000 },
  { id: "CC-4400", name: "Facilities", owner: "Olga Ivanova", monthlyBudget: 72_000 },
];

export default function ActionsInRows() {
  return (
    <Stack gap={2} style={{ maxWidth: 420 }}>
      {COST_CENTRES.slice(0, 4).map((centre) => (
        <Stack key={centre.id} direction="row" gap={3} align="center" justify="space-between">
          <Text size="sm">
            {centre.name} <Text as="span" size="xs" tone="muted">{centre.owner}</Text>
          </Text>
          <Button size="sm" variant="ghost">
            Edit budget
            <VisuallyHidden> of {centre.name}</VisuallyHidden>
          </Button>
        </Stack>
      ))}
    </Stack>
  );
}

When to use something else

Keyboard

Key Action
Tab Reaches a focusable one and shows it while it has focus.

API

VisuallyHiddenProps<E extends ElementType = "span">

NameTypeDefaultDescription
asE"span"Element to render as.

Shown in: Add a skip link

focusablebooleanfalseBecomes visible as soon as the content receives the focus (skip link).

Shown in: Add a skip link

refRef<HTMLElement>—Receives the component's root element. from RefProp

Shown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more

Also takes every attribute of the chosen element – without as, ref.

Known limits

What umriss deliberately does not build, and why: ADR-0032.

Every page of @umriss-ui/core

Getting started

Customising

Layout

Typography

Actions

Forms

Choosing

Dates and times

Feedback

Overlays

Navigation

Data display

API index