Audience
betaWho a message goes to, said before it is sent, with messages for juniors, people under 18, going to their parents or guardians.
Preview
one adult
Goes to Josh Tait
a junior
Goes to Kate and Mark Clarke, instead of Ella Clarke
copy
Goes to Ella Clarke, Kate and Mark Clarke copied
a group
Goes to 12 people, Juniors' parents get it instead
Noor Haddad won't get it: no parent on file.
no parent
Noor Haddad won't get it: no parent on file.
narrow
Goes to 5 people, Juniors' parents get it instead
sizes
Goes to Kate and Mark Clarke, instead of Ella Clarke
Goes to Kate and Mark Clarke, instead of Ella Clarke
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/audience
Use it for
- Above a message, before it is sent, so the sender knows who will read it.
- Pass everyone the message is for in to. Mark juniors and give their guardians.
- Set juniorMessages from your app's setting. "guardians" sends juniors' messages to their parents instead. "copy" sends them to the junior with their parents copied.
- Name a group with toLabel, like Group A. It titles the peek on the count.
- Pass onAddGuardian so a junior with no parent on file comes with the fix.
- Use audienceReach(to, juniorMessages) for the same count elsewhere, like on a Send button.
Avoid it for
- Picking who a message goes to. This says who it reaches. It does not choose.
- A list of people who already read a message. Use Avatar Stack with a count.
Props
- to
Required. Who the message is for. A parent of two juniors is counted once: by id, then email, then name.
{ person: string; photo?: string; id?: string; email?: string; junior?: boolean; guardians?: { person: string; photo?: string; id?: string; email?: string }[] }[]
- toLabel
What the group is called, like Group A. It titles the count's peek.
string
- juniorMessages
Where messages to juniors go: to their parents instead, or to the junior with their parents copied.
"guardians" | "copy" · default "guardians"
- onAddGuardian
The fix for a junior with no parent on file, like opening their profile.
(junior: { person: string; photo?: string }) => void
- size
Text and face size: meta, body, or section type.
"sm" | "md" | "lg" · default "md"
- className
Extra classes on the root. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| to | { person: string; photo?: string; id?: string; email?: string; junior?: boolean; guardians?: { person: string; photo?: string; id?: string; email?: string }[] }[] | - | Required. Who the message is for. A parent of two juniors is counted once: by id, then email, then name. |
| toLabel | string | - | What the group is called, like Group A. It titles the count's peek. |
| juniorMessages | "guardians" | "copy" | "guardians" | Where messages to juniors go: to their parents instead, or to the junior with their parents copied. |
| onAddGuardian | (junior: { person: string; photo?: string }) => void | - | The fix for a junior with no parent on file, like opening their profile. |
| size | "sm" | "md" | "lg" | "md" | Text and face size: meta, body, or section type. |
| className | string | - | Extra classes on the root. Later classes win. |
Accessibility
- Plain text: Goes to, then who. Faces are hidden from screen readers, since the names are in the text.
- A group's count is a peek trigger with a dotted underline. Focus or hover opens everyone, grouped by how they get it.
- The · before a phrase is hidden from screen readers, which hear a comma instead. When the phrase wraps, the · hides too.
- A junior it does not reach is said in danger text, with a button for the fix. It never relies on the color alone.
Examples
One person
<Audience to={[{ person: "Josh Tait" }]} />A junior, through their parents
<Audience
to={[{ person: "Ella Clarke", junior: true, guardians: [{ person: "Kate Clarke" }, { person: "Mark Clarke" }] }]}
/>A group, with a junior who has no parent on file
<Audience
to={group}
toLabel="Group A"
juniorMessages="copy"
onAddGuardian={(junior) => openProfile(junior.person)}
/>