Noticescomponent

Notices({ position = "bottom-right", className, ...props }: NoticesProps): ReactElement
ParamType
propsNoticesProps
Props for <Notices> — the corner for the list, and flex styling variants for the notices container. required
    .positionNoticesPosition
Corner of the screen for the list. New notices slide in from this side. Defaults to "bottom-right"
Return
ReactElement

Render the global list of notices and subscribe to incoming "notice" events.

  • Listens for "notice" events on window (or that bubble up to window) and shows them in the global notice list.
  • This is how e.g. <Button> and <FormNotify> components send notices into the global list.
  • Shows the list in the bottom-right corner, or in the corner that position sets.
  • Each notice slides in and out in its own view transition, from the side of its corner. The other notices move to their new places in the same transition.

Renders the global list of active notices and subscribes to incoming "notice" events. It listens for "notice" events on window (dispatched by the notify() helpers) and shows each one as a <Notice> — this is how components like <Button> and <FormNotify> send notices into the global list.

Things to know:

  • Mount <Notices> once near the root of your app. It renders at that point in the DOM and listens automatically — no context required.
  • Notices auto-dismiss after a short delay unless they carry a "loading" status.
  • Backed by the NOTICES store singleton; for advanced use you can keep a reference to a notice to update or close it manually.
  • The list shows in the bottom-right corner. Set position to "top-left", "top-right", "bottom-left", or "bottom-right" to choose a different corner.
  • Notices animate with view transitions. A new notice slides in from the side of its corner, and a closed notice slides out to that side. The other notices move to their new places. A notice that changes in place (for example from loading to success) changes at once, with no animation.
  • With reduced motion, notices fade in and out in place, and the others move at once. A browser without view transitions shows the changes at once.

Usage

Mount once near the root of your app:

tsx
import { Notices } from "shelving/ui";

export function AppLayout({ children }) {
  return (
    <>
      {children}
      <Notices />
    </>
  );
}

Show the list in the top-left corner:

tsx
<Notices position="top-left" />

Dispatch notices from anywhere — no context required:

tsx
import { notifySuccess, notifyError, callNotified } from "shelving/ui";

notifySuccess("Profile updated.");
notifyError("Could not connect.");

// Wrap an async callback — dispatches success or error automatically.
callNotified(async () => {
  await saveProfile(data);
  return "Profile updated.";
});

Programmatic control via the NOTICES singleton:

tsx
import { NOTICES } from "shelving/ui";

// Show a loading notice and hold a reference to it.
const notice = NOTICES.show(undefined, "loading");
await uploadFile(file);
notice.show("Upload complete.", "success"); // Update in place.
notice.close(); // Or close it immediately.

Styling

Notices positions the list in the corner that position sets. Each notice is only as wide as its content and is aligned to the side of that corner. The list is never wider than --notices-width, and keeps --notices-offset clear at both sides on a narrow screen. The offset also adds the safe area of the device (env(safe-area-inset-*)). Clicks pass through the gaps between notices to the page. Each item is a <Notice>, which has its own hooks. Override these hooks at :root (or any ancestor scope) to retheme.

VariableStylesDefault
--notices-offsetDistance of the list from the top or bottom edge, and from the left and right edgesvar(--space-normal) (16px)
--notices-widthMaximum width of the list and of each noticevar(--width-narrow)
--notices-shadowDrop shadow under each notice in the list (sets --notice-shadow)var(--shadow-normal)
--notices-gapGap between noticesvar(--space-small) (12px)
--notices-transition-durationLength of the slide in, slide out, and movevar(--duration-fast) (150ms)

Global tokens it reads — move these to retheme broadly: --space-normal, --space-small, --shadow-normal, --width-narrow, and --duration-fast.