Skip to content
txbrendan/ui

Audience

beta

Who 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 , 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 , 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

Goes to Kate and Mark Clarke, instead of Ella Clarke

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. 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

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)}
/>