avatar-background-color
Every avatar surface in this repo paints a deterministic background derived from the user's display name, mirroring decentraland/unity-explorer's NameColorHelper.GetNameColor (referenced from ADR-292's avatar rendering pipeline). Same algorithm in browser and in-world = visually consistent identity.
The algorithm (do not re-implement, import the helpers)
src/utils/avatarColor.ts:
getDisplayName({ name, hasClaimedName, ethAddress })— strips non-alphanumeric Unicode, appends#<last4 of address>for unclaimed names, mirroringProfile.CompactInfo.GenerateAndValidateName.getAvatarBackgroundColor(displayName)— FNV-1a 32-bit hash → hue → HSV(hue, 0.75, 1) →#rrggbb.
The hooks that already wire this up:
useProfileAvatar(address)returnsbackgroundColor: string.useCreatorAvatar(address, name)anduseCreatorProfile(address, userName)propagate it; the latter overrides it with the Foundation brand violet (#9d76e3) whenisDclFoundationCreator(userName)matches.
Never re-derive the color in a component — always pull from the hook (when one of the avatar hooks already runs) or call getAvatarBackgroundColor(getDisplayName(...)) once at the parent level.
Application patterns
1. Direct <AvatarFace /> from decentraland-ui2
AvatarFace's prop type does not expose sx or a background prop, so passing sx={{ backgroundColor }} is a TypeScript error. Wrap it in a styled container that targets & .MuiAvatar-root (see src/components/Home/CatchTheVibe/CatchTheVibe.styled.ts:UserInfo):
const UserInfo = styled(Box, { shouldForwardProp: prop => prop !== '$avatarBackgroundColor' })<{ $avatarBackgroundColor?: string }>(
({ $avatarBackgroundColor }) => ({
...($avatarBackgroundColor && {
'&& .MuiAvatar-root': { backgroundColor: $avatarBackgroundColor }
})
})
)
&& doubles class specificity so it overrides ui2's themed secondary.light default.
2. <EventCard avatar={...} /> (avatar inherited from ui2)
EventCard renders AvatarFace internally — same & .MuiAvatar-root override at the wrapper level. See src/components/Home/WhatsOn/WhatsOn.styled.ts:CardWrapper. If the wrapper already hides the avatar (e.g. LiveCardWrapper sets display: none on the avatar slot) skip the override entirely.
3. Custom styled <img> / <div> avatars (Navbar, AdminPermissionsModal, jump Card, LiveNowCard)
Forward the color via styled-component prop or inline style={{ backgroundColor }}. Pattern: filter the prop with shouldForwardProp so it doesn't leak to the DOM:
const CreatorAvatar = styled('img', { shouldForwardProp: prop => prop !== 'avatarBackgroundColor' })<{ avatarBackgroundColor?: string }>(
({ avatarBackgroundColor }) => ({
backgroundColor: avatarBackgroundColor ?? '#f0f0f0' /* sensible fallback for the placeholder */
})
)
For sites that already pass other style props through (e.g. LandingNavbar's AvatarButton), inline style={{ backgroundColor: avatarBackgroundColor }} is fine and avoids touching the styled-component signature.
Inputs by call site
| Site | How to source the color |
|---|---|
Has useProfileAvatar |
Destructure { backgroundColor }. |
Has useCreatorAvatar |
Destructure { backgroundColor }. Override to #9d76e3 when card.isGenesisPlaza is true. |
Has useCreatorProfile |
Destructure { backgroundColor } — Foundation override is already applied inside the hook. |
| Static profile data only | Compute inline with getAvatarBackgroundColor(getDisplayName({ name, hasClaimedName, ethAddress })). Wrap in useMemo keyed on those three fields. |
| No profile fetched yet | Call useProfileAvatar(address, { skip: !address }) at the parent. Cheap — query is cached. |
Tests to add when introducing a new avatar surface
- A spec that mocks the hook to return a known
backgroundColorand asserts the styled DOM applies it. - For Foundation-overridden surfaces, assert
#9d76e3paints whenisGenesisPlaza/isDclFoundationCreatormatches.
Don't
- Don't re-introduce
getCreatorColor(address). The legacy djb2-based helper was removed because its hue was derived from the address only — Unity uses the display name (Validated#xxxxor claimed-name) so the colors didn't match between surfaces. - Don't hardcode the Foundation override hex; import
DCL_FOUNDATION_BACKGROUND_COLORfromsrc/utils/avatarColor.ts. The navbar's loading shimmer (#FF4BED) is the only pre-existing palette literal allowed for an avatar surface, and only on the loading-state class. - Don't call
getAvatarBackgroundColoron a raw username without first runninggetDisplayName(...)— Unity validates and suffixes before hashing, so skipping that step diverges on every name with whitespace, punctuation, or no claimed name.
References
- ADR-292: https://adr.decentraland.org/adr/ADR-292 — describes the explorer rendering pipeline; the algorithm itself is in
unity-explorer/Explorer/Assets/DCL/Infrastructure/Utility/NameColorHelper.csand the display-name validation inProfile.CompactInfo.GenerateAndValidateName(same repo). - Helper + spec:
src/utils/avatarColor.ts,src/utils/avatarColor.spec.ts.