Props
Usage guidelines
- For the general display of groups of people, companies and/or brands.
- In cases where an affordance for adding collaborators is needed.
Best Practices
Use the default alternative if no image source is available. This should be the first character of the provided name.
Use alternative graphics or icons
Use AvatarGroup to represent a group of people and/or organizations.
Use AvatarGroup to represent metaphorical ideas, like multiple Boards or trends. Instead, consider an Image or the appropriate interactive component.
Accessibility
ARIA attributes
AvatarGroup requires accessibilityLabel
. AvatarGroup is a group of elements that require a parent label describing both the data presented and the call to action in the case of button and link roles. As seen in the example below, the screen-reader reads: "Collaborators: Keerthi, Alberto, and 10 more. Add collaborators to this board."
If AvatarGroup is used as a control button to show/hide Popover-component, we recommend passing the following ARIA attributes to assist screen readers:
accessibilityControls
: informs the screen reader that AvatarGroup controls the display of an anchored Popover-component. It populates aria-controls.accessibilityHaspopup
: informs the screen reader that there’s a Popover-component attached to AvatarGroup. It populates aria-haspopup.accessibilityExpanded
: informs the screen reader whether an anchored Popover-component is currently open or closed. It populates aria-expanded.
import { useEffect, useRef, useState } from 'react'; import { AvatarGroup, Box, Flex, Layer, Popover, SearchField, Text, useDangerouslyInGestaltExperiment, } from 'gestalt'; function SearchCollaboratorsField() { const ref = useRef(null); useEffect(() => { if (ref.current) ref.current.focus(); }, []); return ( <SearchField ref={ref} accessibilityLabel="Search other users" id="searchField" onChange={() => {}} placeholder="Search by name or email" size="lg" /> ); } export default function Example() { const [open, setOpen] = useState(false); const anchorRef = useRef(null); const isInVRExperiment = useDangerouslyInGestaltExperiment({ webExperimentName: 'web_gestalt_visualRefresh', mwebExperimentName: 'web_gestalt_visualRefresh', }); const collaborators = [ { name: 'Keerthi', src: 'https://i.ibb.co/ZfCZrY8/keerthi.jpg', }, { name: 'Alberto', src: 'https://i.ibb.co/NsK2w5y/Alberto.jpg', }, ...new Array(10), ]; const collaboratorsVR = [ { name: 'Fatima', src: 'https://i.pinimg.com/originals/bf/bc/27/bfbc27685d81eb9a8f65c201ea661f0e.jpg', }, { name: 'Ayesha', src: 'https://i.pinimg.com/originals/c5/5c/ac/c55caca43a7c16766215ec165b649c1c.jpg', }, ...new Array(10), ]; const names = isInVRExperiment ? 'Fatima, Ayesha,' : 'Keerthi, Alberto,'; return isInVRExperiment ? ( <Flex height="100%" width="100%"> <Box height="200" marginTop={6} padding={2}> <AvatarGroup ref={anchorRef} accessibilityExpanded={open} accessibilityLabel={`Collaborators: ${names} and 10 more. Add collaborators to this board.`} addCollaborators collaborators={collaboratorsVR} onClick={() => setOpen((value) => !value)} role="button" size="md" /> </Box> {open && ( <Layer> <Popover anchor={anchorRef.current} idealDirection="down" onDismiss={() => setOpen(false)} positionRelativeToAnchor={false} size={500} > <Box flex="grow" marginBottom={8} marginEnd={4} marginStart={4} marginTop={6} width={360} > <Flex direction="column" gap={{ column: 4, row: 0 }}> <Text align="center" color="default" weight="bold"> Invite collaborators </Text> <SearchCollaboratorsField /> </Flex> </Box> </Popover> </Layer> )} </Flex> ) : ( <Flex height="100%" width="100%"> <Box height="200" marginTop={6} padding={2}> <AvatarGroup ref={anchorRef} accessibilityExpanded={open} accessibilityLabel={`Collaborators: ${names} and 10 more. Add collaborators to this board.`} addCollaborators collaborators={isInVRExperiment ? collaboratorsVR : collaborators} onClick={() => setOpen((value) => !value)} role="button" size="md" /> </Box> {open && ( <Layer> <Popover anchor={anchorRef.current} idealDirection="down" onDismiss={() => setOpen(false)} positionRelativeToAnchor={false} size="xl" > <Box flex="grow" marginBottom={8} marginEnd={4} marginStart={4} marginTop={6} width={360} > <Flex direction="column" gap={{ column: 6, row: 0 }}> <Text align="center" color="default" weight="bold"> Invite collaborators </Text> <SearchCollaboratorsField /> </Flex> </Box> </Popover> </Layer> )} </Flex> ); }
Keyboard Interaction
If AvatarGroup is acting as a button or link, the Tab key will focus the AvatarGroup.
Hitting the Enter or Return key opens a dialog or redirects to a new page (depending on the role) and the user can then add or view collaborators.
Localization
Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.
Variants
Fixed sizes
AvatarGroup is available in 3 fixed height sizes: xs
(24px), sm
(32px), and md
(48px).
Responsive sizing
AvatarGroup is a responsive component. Avatar Groups that are not given a size prop or use size fit
will expand to fit to the width of their parent container. A common use case is to achieve column-based sizing. Resize the width or number of avatars to see the AvatarGroup change to match the width of the Column it's been placed in.
import { AvatarGroup, Box, Flex, Link, Text } from 'gestalt'; export default function Example() { return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <Box height={100} padding={2} width={600}> <Flex alignItems="center" height="100%"> <Box column={5} height="100%"> <AvatarGroup accessibilityLabel="Collaborators: Keerthi, Alberto, and Shanice." collaborators={[ { name: 'Keerthi', src: 'https://i.ibb.co/ZfCZrY8/keerthi.jpg', }, { name: 'Alberto', src: 'https://i.ibb.co/NsK2w5y/Alberto.jpg', }, { name: 'Shanice', src: 'https://i.ibb.co/7tGKGvb/shanice.jpg', }, ]} size="fit" /> </Box> <Box column={7} marginStart={2}> <Text inline>The </Text> <Text inline weight="bold"> <Link display="inlineBlock" href="https://www.pinterest.com/search/boards/?q=quick%20vegan%20recipes&rs=typed&term_meta[]=quick%7Ctyped&term_meta[]=vegan%7Ctyped&term_meta[]=recipes%7Ctyped" > Quick Vegan Recipes{' '} </Link> </Text> <Text inline> board has 3 followers.</Text> </Box> </Flex> </Box> </Flex> ); }
Collaborators display
AvatarGroup displays up to three user avatars. More users, if present, will be displayed as a numerical count. Not available for 'xs' size.
Role
AvatarGroup can be display only, but can also act as a button or link. It will only be clickable if role is set to button
or link
. For button role, onClick
is required. For link role, href
is required.
import { useRef, useState } from 'react'; import { Avatar, AvatarGroup, Box, Flex, Layer, Popover, Text, useDangerouslyInGestaltExperiment, } from 'gestalt'; export default function Example() { const [open, setOpen] = useState(false); const anchorRef = useRef(null); const isInVRExperiment = useDangerouslyInGestaltExperiment({ webExperimentName: 'web_gestalt_visualRefresh', mwebExperimentName: 'web_gestalt_visualRefresh', }); const collaborators = [ { name: 'Keerthi', src: 'https://i.ibb.co/ZfCZrY8/keerthi.jpg', }, { name: 'Alberto', src: 'https://i.ibb.co/NsK2w5y/Alberto.jpg', }, { name: 'Shanice', src: 'https://i.ibb.co/7tGKGvb/shanice.jpg', }, ]; const collaboratorsVR = [ { name: 'Fatima', src: 'https://i.pinimg.com/originals/bf/bc/27/bfbc27685d81eb9a8f65c201ea661f0e.jpg', }, { name: 'Sora', src: 'https://i.pinimg.com/originals/ab/c5/4a/abc54abd85df131e90ca6b372368b738.jpg', }, { name: 'Ayesha', src: 'https://i.pinimg.com/originals/c5/5c/ac/c55caca43a7c16766215ec165b649c1c.jpg', }, ]; return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <AvatarGroup ref={anchorRef} accessibilityLabel="Click to see group collaborators." collaborators={isInVRExperiment ? collaboratorsVR : collaborators} onClick={() => setOpen((value) => !value)} role="button" size="md" /> {open && ( <Layer> <Popover anchor={anchorRef.current} idealDirection="down" onDismiss={() => setOpen(false)} positionRelativeToAnchor={false} size="xs" > <Box flex="grow" marginBottom={8} marginEnd={4} marginStart={4} marginTop={6} width={360} > <Flex direction="column" gap={{ column: 6, row: 0 }}> <Text align="start" color="default" weight="bold"> Collaborators </Text> <Flex direction="column" gap={{ column: 2, row: 0 }}> {!isInVRExperiment && collaborators.map(({ name, src }) => ( <Flex key={name} alignItems="center" gap={{ row: 2, column: 0 }} > <Avatar name={name} size="md" src={src} /> <Text weight="bold">{name}</Text> </Flex> ))} {isInVRExperiment && collaboratorsVR.map(({ name, src }) => ( <Flex key={name} alignItems="center" gap={{ row: 2, column: 0 }} > <Avatar name={name} size="md" src={src} /> <Text weight="bold">{name}</Text> </Flex> ))} </Flex> </Flex> </Box> </Popover> </Layer> )} </Flex> ); }
import { AvatarGroup, Flex, useDangerouslyInGestaltExperiment } from 'gestalt'; export default function Example() { const isInVRExperiment = useDangerouslyInGestaltExperiment({ webExperimentName: 'web_gestalt_visualRefresh', mwebExperimentName: 'web_gestalt_visualRefresh', }); const collaborators = [ { name: 'Keerthi', src: 'https://i.ibb.co/ZfCZrY8/keerthi.jpg', }, { name: 'Alberto', src: 'https://i.ibb.co/NsK2w5y/Alberto.jpg', }, { name: 'Shanice', src: 'https://i.ibb.co/7tGKGvb/shanice.jpg', }, ]; const collaboratorsVR = [ { name: 'Fatima', src: 'https://i.pinimg.com/originals/bf/bc/27/bfbc27685d81eb9a8f65c201ea661f0e.jpg', }, { name: 'Sora', src: 'https://i.pinimg.com/originals/ab/c5/4a/abc54abd85df131e90ca6b372368b738.jpg', }, { name: 'Ayesha', src: 'https://i.pinimg.com/originals/c5/5c/ac/c55caca43a7c16766215ec165b649c1c.jpg', }, ]; return ( <Flex alignItems="center" height="100%" justifyContent="center" width="100%" > <AvatarGroup accessibilityLabel="Visit group activity board." collaborators={isInVRExperiment ? collaboratorsVR : collaborators} href="#Role" onClick={() => {}} role="link" size="md" /> </Flex> ); }
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Ready | Component is available in Figma for web and mobile web. |
Responsive Web | Ready | Component responds to changing viewport sizes in web and mobile web. |
Related
Avatar
Avatar is the ideal component in cases where only one person or brand needs to be displayed.