Перейти к содержимому

Аватар

Аватар пользователя с запасным вариантом в виде инициалов для меню профилей, цепочек комментариев и списков команд — с толстой рамкой в стиле «необрутализм».

import {
  Avatar,
  AvatarBadge,

Аватар показывает изображение пользователя, а пока картинка грузится или если она не загрузилась — подставляет AvatarFallback с инициалами или иконкой, не сдвигая вёрстку. Построен на компоненте Base UI Avatar и оформлен по рецепту необрутализма: толстая рамка в 2px и инициалы жирным заголовочным шрифтом.

Когда это пригодится:

  • Меню профиля — аватар в шапке, который открывает действия с аккаунтом; смотрите пример Выпадающее меню.
  • Комментарии и лента активности — лицо рядом с каждым автором, а инициалы прикрывают битые и отсутствующие фото.
  • Списки команд и участников — наложите коллег друг на друга через AvatarGroup и ограничьте остаток с помощью AvatarGroupCount.

Установка

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/avatar.json
npx shadcn@latest add https://neobrutalism.com/r/base/avatar.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/avatar.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/avatar.json

Установите следующие зависимости:

pnpm add @base-ui/react
npm install @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

Скопируйте и вставьте следующий код в свой проект.

components/ui/avatar.tsx
"use client"

import * as React from "react"
import { Avatar as AvatarPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Avatar({
  className,
  size = "default",
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
  size?: "default" | "sm" | "lg"
}) {
  return (
    <AvatarPrimitive.Root
      data-slot="avatar"
      data-size={size}
      className={cn(
        "group/avatar relative flex size-8 shrink-0 rounded-full border-2 select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
        className
      )}
      {...props}
    />
  )
}

function AvatarImage({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
  return (
    <AvatarPrimitive.Image
      data-slot="avatar-image"
      className={cn(
        "aspect-square size-full rounded-full object-cover",
        className
      )}
      {...props}
    />
  )
}

function AvatarFallback({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
  return (
    <AvatarPrimitive.Fallback
      data-slot="avatar-fallback"
      className={cn(
        "flex size-full items-center justify-center rounded-full bg-muted font-head text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
        className
      )}
      {...props}
    />
  )
}

function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
  return (
    <span
      data-slot="avatar-badge"
      className={cn(
        "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
        "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
        "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
        "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
        className
      )}
      {...props}
    />
  )
}

function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="avatar-group"
      className={cn(
        "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
        className
      )}
      {...props}
    />
  )
}

function AvatarGroupCount({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="avatar-group-count"
      className={cn(
        "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
        className
      )}
      {...props}
    />
  )
}

export {
  Avatar,
  AvatarImage,
  AvatarFallback,
  AvatarGroup,
  AvatarGroupCount,
  AvatarBadge,
}

Обновите пути импорта в соответствии со структурой вашего проекта.

Использование

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
<Avatar>
  <AvatarImage src="https://github.com/shadcn.png" />
  <AvatarFallback>CN</AvatarFallback>
</Avatar>

Композиция

Используйте следующую композицию, чтобы собрать Avatar:

Avatar
├── AvatarImage
├── AvatarFallback
└── AvatarBadge

Используйте следующую композицию, чтобы собрать AvatarGroup:

AvatarGroup
├── Avatar
│   ├── AvatarImage
│   ├── AvatarFallback
│   └── AvatarBadge
├── Avatar
│   ├── AvatarImage
│   ├── AvatarFallback
│   └── AvatarBadge
└── AvatarGroupCount

Примеры

Базовый

Базовый компонент аватара с изображением и фолбэком.

NB
import {
  Avatar,
  AvatarFallback,

Badge

Используйте компонент AvatarBadge, чтобы добавить бейдж на аватар. Бейдж располагается в правом нижнем углу аватара.

import {
  Avatar,
  AvatarBadge,

Используйте проп className, чтобы задать бейджу собственные стили — цвета, размеры и так далее.

<Avatar>
  <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
  <AvatarFallback>CN</AvatarFallback>
  <AvatarBadge className="bg-green-600 dark:bg-green-800" />
</Avatar>

Badge с иконкой

Вы также можете использовать иконку внутри <AvatarBadge>.

import { PlusIcon } from "lucide-react"

import {

Группа аватаров

Используйте компонент AvatarGroup, чтобы добавить группу аватаров.

import {
  Avatar,
  AvatarFallback,

Счётчик группы аватаров

Используйте <AvatarGroupCount>, чтобы добавить в группу счётчик.

import {
  Avatar,
  AvatarFallback,

Группа аватаров с иконкой

Вы также можете использовать иконку внутри <AvatarGroupCount>.

import { PlusIcon } from "lucide-react"

import {

Размеры

Используйте проп size, чтобы изменить размер аватара.

import {
  Avatar,
  AvatarFallback,

Выпадающее меню

Вы можете использовать компонент Avatar как триггер для выпадающего меню.

"use client"

import {

RTL

Чтобы включить поддержку RTL в Neobrutalism, см. руководство по настройке RTL.

"use client"

import * as React from "react"

Доступность

Аватар — это изображение, поэтому работают обычные правила для картинок: задайте AvatarImage атрибут alt с именем человека, если аватар — единственное, что его называет, и alt="", если имя написано рядом, иначе программы чтения с экрана произнесут его дважды. Инициалы в AvatarFallback — обычный текст, его тоже читают вслух, так что пусть они остаются осмысленными. Если аватар открывает меню (смотрите Выпадающее меню), доступное имя должно быть у кнопки-триггера, а не у изображения.

Справочник API

Avatar

Компонент Avatar — корневой компонент, который оборачивает изображение аватара и фолбэк.

PropTypeDefault
size"default" | "sm" | "lg""default"
classNamestring-

AvatarImage

Компонент AvatarImage показывает изображение аватара. Он принимает все пропы Base UI Avatar Image.

PropTypeDefault
srcstring-
altstring-
classNamestring-

AvatarFallback

Компонент AvatarFallback показывает фолбэк, когда изображение не удаётся загрузить. Он принимает все пропы Base UI Avatar Fallback.

PropTypeDefault
classNamestring-

AvatarBadge

Компонент AvatarBadge показывает индикатор-бейдж на аватаре, обычно в правом нижнем углу.

PropTypeDefault
classNamestring-

AvatarGroup

Компонент AvatarGroup показывает группу аватаров с наложением.

PropTypeDefault
classNamestring-

AvatarGroupCount

Компонент AvatarGroupCount показывает индикатор-счётчик в группе аватаров, обычно отображая количество дополнительных аватаров.

PropTypeDefault
classNamestring-

Подробнее о пропах Base UI Avatar см. в документации Base UI.