Skip to content

Built · v1.0 · Avatar · Badge

Avatar & Badge

Two separate sets that spend most of their life in the same place: the leading slot of a List Row. The Avatar says who, the Badge says how many. Neither one positions the other; that is the consumer’s job.

Photo
JDText
Icon
AR+ Dot
MK2+ Count

Use when

  • Identifying a person or a number in a list, a header or a thread.
  • Marking that something is unread or unseen, with a Dot.
  • Counting something small and actionable, with a Count.

Don’t use when

  • Labelling status or category with words. That is a Chip, and it does not exist yet.
  • Showing a number that is information rather than a prompt. Use the trailing value slot of a List Row.
  • Decorating. An avatar with no identity behind it is noise.

40 × 40, radius/full, content clipped. One variant property, Content, with three values.

ContentBackgroundForegroundFor
PhotoNeutral/200 placeholderthe image itselfanyone with a picture
Textavatar/identity/1…6avatar/onIdentity, Inter Medium 14 / 20initials, or the last digits of an unknown number
Iconscheme/surfaceContainerHighestsilhouette on text/secondarysystem and non-human rows

The identity colours mean nothing, on purpose

Section titled “The identity colours mean nothing, on purpose”

The six avatar/identity stops are assigned deterministically from the contact id, never from anything semantic. Two people get different colours so you can tell them apart at a glance in a long list, and the same person keeps their colour forever. A colour never means “VIP” or “blocked” or “unread”; the moment it does, the list stops being scannable.

JDidentity/1
ARidentity/2
MKidentity/3
TSidentity/4
LPidentity/5
99identity/6

All six are mode-independent: they hold the same value in Light, Carbon and Navy. That is deliberate. A contact whose avatar changed colour when you switched theme would stop being recognisable, which is the one job the colour has.

White initials clear 4.5:1 on every one of the six. The tightest is identity/3 at 6.13, and the most generous is identity/2 at 14.66, so there is real headroom on all of them.

The indicator only. It does not know what it is attached to, exactly like Flutter’s native Badge. Both variants fill with feedback/error and carry a 1.5 px stroke in scheme/surface so they punch a hole in whatever they overlap rather than muddying into it.

ContentSizeLabel
Dot8 × 8none
Counthugs, min 17 × 18, padding 1 / 5feedback/onError, Inter Medium 11 / 16

That stroke is the whole trick. Without it a red dot on a red-ish photo disappears; with it, the badge always has a surface-coloured ring separating it from its background, in every mode.

Red here is a counter, not an error. feedback/error owns four things: validation, the destructive confirm inside a dialog, negative amounts, and counter badges. An unread count is not a failure, it is just the one strong colour we allow for “look here”. See Colour as signage.

One badge per avatar. A dot and a count on the same avatar is two answers to one question.

Counts cap at 99+. Past that the pill grows wider than the avatar it sits on, and the exact number stopped being useful a long time before.

The avatar is never the tap target on its own. In a list row the whole row is tappable. An avatar that opens a profile while the row opens a thread gives you two targets 40 px apart, which fails at the 360 dp floor.

Photo needs a fallback, always. Text is the fallback for people, Icon is the fallback for everything that is not a person. A broken image is never acceptable, because the avatar is often the only thing distinguishing two rows.

avatar/identity/1…6avatar/onIdentityscheme/surfaceContainerHighesttext/secondary → silhouettefeedback/error → badge fillfeedback/onError → countradius/full

The seven avatar/ tokens were added in July 2026 and are the newest group in the system. They are also the only group where every token already carries a scope and a description, which is the model the rest of the system is meant to follow.