Push, in-app banners, toasts, and badges — everything the app says without being asked.
Part of the Konjo design language. The laws in the spine apply here unless this chapter contradicts them.
Before you write a word of consequence copy, read product-facts.md. Who is alerted, what becomes permanent, who can read it afterwards and whether it can be undone are decided there — and where they are not, that is a blocker rather than something to phrase well.
Konjo is an app about a place people physically attend. Every notification competes with a dojo that already texts its students, and the fastest way to be uninstalled is to be the app that nags. A dojo does not guilt people into attending, and neither does its software.
The four channels
| Channel | For | Dismissal |
|---|---|---|
| Push | Something happening away from the app that has a time cost if missed | The OS |
| In-app banner | Persistent state the screen must account for — offline, a pending action, a blocked feature | Only when the state resolves |
| Toast | Confirmation of something the person just did | Itself, after a few seconds |
| Badge | A count of things waiting | Reading them |
Using the wrong one is what makes an app feel noisy: a toast for something that needs a decision, a banner for something transient, a push for something that could have waited for the badge.
Push
The test: would a person be annoyed to have learned this an hour later? If not, it is not a push.
Send:
- Someone did something to them — coached their video, replied, promoted them.
- Something they signed up for is imminent, once.
- Something needs their action and has a deadline — a waiver before a test.
Never send:
- A streak reminder, a "we miss you", or anything that leverages loss aversion. This is a settled decision, not a preference — see progress.
- Marketing, feature announcements, or "did you know".
- Anything they will see anyway next time they open the app.
- More than one per event. If three people comment, that is one notification.
Copy: the subject leads, the app is invisible. "Sensei Marcus coached your side kick" — not "Konjo: You have new feedback!". No exclamation marks, no emoji, no first person from the app. It is the same voice as everywhere else, at half the length.
Every push lands on the specific thing, never the tab root. See navigation.
In-app banners
A banner is for a state, and it stays until the state ends.
- A banner sits with what it is about. A banner describing the screen's state — offline, a pending action, a feature switched off — is full width at the top of the content, in flow, never floating and never over the header. A banner describing what just happened to a control — a submit that failed — sits with that control, which on a form means directly above the primary; see forms. Same component, and the position is the difference between "this screen is in a state" and "your tap did not work".
- The feedback fill with its matching 1px border,
captiontext. Fill alone is ~1.02:1 against the page and reads as a slightly different background. - One banner at a time. Two stacked banners is a screen with a problem the banners are not going to solve.
- A banner offering an action offers exactly one, as a text link.
ValidationBanneralready exists — extend it rather than writing another.
Toasts
- Confirmation only, for something the person just did. "Session logged." Past tense, plain, naming what happened.
- Never a toast for an error that needs a decision — that is a banner or an inline error, because a toast disappears and a decision does not.
- Never a toast for something the screen already shows. If the row appeared, the row is the confirmation.
- Undo lives in the toast when the action is reversible, and the toast waits longer when it carries one.
- Use
ToastProvider/useToast. Do not build a second one. - Announce it:
accessibilityLiveRegion="polite", or it does not exist for a screen-reader user.
Badges
- Count things a person can act on, not things that merely happened.
- Caps at 9+ in chrome, 99+ elsewhere — see content format.
- Reading clears it. A badge that survives being read is a badge people learn to ignore, and it takes every other badge with it.
- The tab bar's badge is chrome and is exempt from the red budget — it is the one place a red dot is not spending the screen's red role.
Preferences
- Every push category is individually switchable, and the settings screen uses the same words the notifications do.
- Turning a category off turns it off. There is no "important" override.
notification_preferencesandpush_tokenare sensitive columns and stay out of the broadusersselect.
Never
- A streak or attendance guilt notification.
- A toast carrying an error that needs a decision.
- Two banners on one screen.
- A badge that does not clear on read.
- Push copy that leads with the app's name.
- A notification that lands on a tab root instead of the thing it is about.