Skip to content

Feedback Patterns

Modeled on Ant Design’s feedback levels: choose the least intrusive surface that still gets the job done. Three levels, from weak to strong.

LevelWhenSurface(s)How it’s shown
Silent / inlineStatus that lives with the content; non-blocking, no acknowledgement needed.InfoBanner, Callout, Badge, StatusDot, inline field errorTextPlaced in the view tree by the developer where the content is.
Transient / globalResult of a user action (saved, copied, failed) that doesn’t block the next step.feedback.toast(…) (built on AlertToast)Auto-dismissing overlay at the app root.
Modal / globalA decision is required before continuing, or a destructive action needs confirmation.feedback.confirm(…) (built on Dialog), BottomSheetDimmed scrim, blocks interaction until resolved.
  • Does the user need to act on it?
    • No, it’s just status → inline (InfoBanner / Callout).
    • No, but it confirms their action happened → toast.
    • Yes, they must choose / confirm → confirm dialog (or BottomSheet for richer choices).
  • Is it tied to one field? → inline errorText, never a toast.
  • Is it destructive?confirm(primaryKind: .error) so the primary button is red.
  • Is it marketing / passive?PromoBanner, not feedback.

One presenter handles the two global levels so call sites don’t wire bindings each time. Install once at the app root, then call from anywhere:

// App root
ContentView().feedbackHost()
// Anywhere below
@EnvironmentObject var feedback: FeedbackPresenter
feedback.toast("Saved", kind: .success) // transient
feedback.toast("No connection", kind: .error)
feedback.confirm( // modal
title: "Cancel reservation?",
message: "This action cannot be undone.",
primaryTitle: "Cancel", primaryKind: .error,
onPrimary: { }
)

FeedbackKind (success / info / warning / error) maps to the token system — the same intent drives the toast color and the confirm dialog’s primary-button color.

Inline surfaces stay developer-placed (they belong in the layout, not over it), so they have no presenter — see InfoBanner / Callout in the component gallery.