---
title: Пузырь
description: Отображает диалоговый контент в пузыре сообщений. Поддерживает варианты, выравнивание, группировку, реакции и сворачиваемый контент.
order: 0 
---
## Bubble: отображает диалоговый контент в виде всплывающего сообщения. Поддерживает варианты, выравнивание, группировку, реакции и сворачиваемый контент.


```python
from components.ui.bubble import Bubble
```

```python
from typing import Any

from reflex.components.component import Component
from reflex.utils.imports import ImportVar
from reflex.vars import FunctionVar, Var
from reflex.vars.base import VarData

PACKAGE_CN = "clsx-for-tailwind@1.0.0"
CN = Var(
    "cn",
    _var_data=VarData(
        imports={
            PACKAGE_CN: ImportVar(tag="cn"),
        },
    ),
).to(FunctionVar)


class CoreComponent(Component):
    unstyled: Var[bool]

    @classmethod
    def set_class_name(
        cls, default_class_name: str | Var[str], props: dict[str, Any]
    ) -> None:

        if "render_" in props:
            return

        props_class_name = props.get("class_name", "")

        if props.pop("unstyled", False):
            props["class_name"] = props_class_name
            return

        props["class_name"] = cn(default_class_name, props_class_name)

    def _exclude_props(self) -> list[str]:
        return [
            *super()._exclude_props(),
            "unstyled",
        ]


def cn(*classes: Var | str | tuple | list | None) -> Var:
    return CN.call(*classes).to(str)
```

```python
from typing import Literal

import reflex as rx
from reflex.components.component import ComponentNamespace

from ..core.core import CoreComponent, cn

BubbleVariant = Literal[
    "default", "secondary", "muted", "tinted", "outline", "ghost", "destructive"
]
BubbleAlign = Literal["start", "end"]
BubbleSide = Literal["top", "bottom"]


class ClassNames:
    GROUP = "flex min-w-0 flex-col gap-2"

    VARIANTS: dict[str, str] = {
        "default": (
            "*:data-[slot=bubble-content]:bg-primary "
            "*:data-[slot=bubble-content]:text-primary-foreground"
        ),
        "secondary": (
            "*:data-[slot=bubble-content]:bg-secondary "
            "*:data-[slot=bubble-content]:text-secondary-foreground"
        ),
        "muted": "*:data-[slot=bubble-content]:bg-muted",
        "tinted": (
            "*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] "
            "*:data-[slot=bubble-content]:text-foreground "
            "dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)]"
        ),
        "outline": (
            "*:data-[slot=bubble-content]:border-border "
            "*:data-[slot=bubble-content]:bg-background"
        ),
        "ghost": (
            "border-none "
            "*:data-[slot=bubble-content]:rounded-none "
            "*:data-[slot=bubble-content]:bg-transparent "
            "*:data-[slot=bubble-content]:p-0"
        ),
        "destructive": (
            "*:data-[slot=bubble-content]:bg-destructive/10 "
            "*:data-[slot=bubble-content]:text-destructive "
            "dark:*:data-[slot=bubble-content]:bg-destructive/20"
        ),
    }

    ROOT = (
        "group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 "
        "group-data-[align=end]/message:self-end "
        "data-[align=end]:self-end "
        "data-[variant=ghost]:max-w-full"
    )

    CONTENT = (
        "w-fit max-w-full min-w-0 overflow-hidden rounded-3xl border border-transparent "
        "px-3 py-2.5 text-sm leading-relaxed break-words "
        "group-data-[align=end]/bubble:self-end"
    )

    REACTIONS_BASE = (
        "absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 "
        "rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card has-[button]:p-0"
    )

    REACTIONS_SIDE: dict[str, str] = {
        "top": "top-0 -translate-y-3/4",
        "bottom": "bottom-0 translate-y-3/4",
    }

    REACTIONS_ALIGN: dict[str, str] = {
        "start": "left-3",
        "end": "right-3",
    }


class NativeBubbleGroup(CoreComponent):
    @classmethod
    def create(cls, *children, **props) -> rx.Component:
        props["data-slot"] = "bubble-group"
        cls.set_class_name(ClassNames.GROUP, props)
        return rx.el.div(*children, **props)


class NativeBubbleRoot(CoreComponent):
    @classmethod
    def create(cls, *children, **props) -> rx.Component:
        variant: BubbleVariant = props.pop("variant", "default")
        align: BubbleAlign = props.pop("align", "start")

        props["data-slot"] = "bubble"
        props["data-variant"] = variant
        props["data-align"] = align

        cls.set_class_name(
            cn(
                ClassNames.ROOT,
                ClassNames.VARIANTS.get(variant, ""),
            ),
            props,
        )
        return rx.el.div(*children, **props)


class NativeBubbleContent(CoreComponent):
    @classmethod
    def create(cls, *children, **props) -> rx.Component:
        props["data-slot"] = "bubble-content"
        cls.set_class_name(ClassNames.CONTENT, props)
        return rx.el.div(*children, **props)


class NativeBubbleReactions(CoreComponent):
    @classmethod
    def create(cls, *children, **props) -> rx.Component:
        side: BubbleSide = props.pop("side", "bottom")
        align: BubbleAlign = props.pop("align", "end")

        props["data-slot"] = "bubble-reactions"
        props["data-align"] = align
        props["data-side"] = side

        cls.set_class_name(
            cn(
                ClassNames.REACTIONS_BASE,
                ClassNames.REACTIONS_SIDE.get(side, ""),
                ClassNames.REACTIONS_ALIGN.get(align, ""),
            ),
            props,
        )
        return rx.el.div(*children, **props)


class Bubble(ComponentNamespace):
    group = staticmethod(NativeBubbleGroup.create)
    root = staticmethod(NativeBubbleRoot.create)
    content = staticmethod(NativeBubbleContent.create)
    reactions = staticmethod(NativeBubbleReactions.create)
    class_names = ClassNames


bubble = Bubble()
```

Отображает диалоговый контент в пузыре сообщений. Поддерживает варианты, выравнивание, группировку, реакции и сворачиваемый контент.

Компонент `Bubble` отображает диалоговый контент в рамке. Используйте его для текста чата, краткого структурированного вывода, цитируемых ответов, предложений и реакций.

Для полнофункциональных интерфейсов чата используйте компонент [§§IC_13§§](/docs/components/message). `Bubble` намеренно ограничен поверхностью пузырька. Размещайте аватары, имена, метки времени, метаданные и действия на уровне сообщений в [§§IC_15§§](/docs/components/message).


# Особенности

- Семь визуальных вариантов: от яркого основного пузырька до призрачного контента без рамки.
- Выравнивание начала и конца пузырьков отправителя и получателя.
- Реакции, которые привязываются к краю пузырька с настраиваемой стороной и выравниванием.
- Размер пузырьков относительно их содержимого, до 80% ширины контейнера
- Полиморфный контент через `render` для ссылок и всплывающих окон кнопок.
- Настраиваемый стиль с помощью реквизита `class_name` на каждой части.

# Примеры

## Варианты

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

**Использованный реквизит:** `variant` на `bubble.root`.

```python
def bubble_with_variants():
    return rx.el.div(
        bubble.root(
            bubble.content("This is the default primary bubble."),
            variant="default",
        ),
        bubble.root(
            bubble.content("This is the secondary variant."),
            variant="secondary",
            align="end",
        ),
        bubble.root(
            bubble.content(
                "This one is muted. It uses a lower emphasis color for the chat bubble."
            ),
            bubble.reactions(
                rx.el.span("👍"),
                role="img",
                aria_label="Reaction: thumbs up",
            ),
            variant="muted",
        ),
        bubble.root(
            bubble.content(
                "This one is tinted. The tint is a softer color derived from the primary color."
            ),
            variant="tinted",
            align="end",
        ),
        bubble.root(
            bubble.content("We can also use an outlined variant."),
            variant="outline",
        ),
        bubble.root(
            bubble.content("Or a destructive variant with a reaction."),
            bubble.reactions(
                rx.el.span("🔥"),
                role="img",
                aria_label="Reaction: fire",
            ),
            variant="destructive",
            align="end",
        ),
        bubble.root(
            bubble.content(
                rx.html(
                    """
                    Ghost bubbles work for assistant text, **markdown**, and other content that should not be framed.

                    This is perfect for assistant messages that should not have a frame and can take the full width of the container. You can also render `code` in it.

                    Ghost bubbles are full width and can take the full width of the container.
                    """,
                    class_name="docs-prose",
                ),
            ),
            variant="ghost",
        ),
        class_name="flex w-full max-w-sm flex-col gap-12 py-12",
    )
```


| Вариант | Описание |
| ------------- | ------------------------------------------------------ |
| `default` | Сильный первичный пузырь, обычно для текущего пользователя. |
| `secondary` | Стандартный нейтральный пузырь для содержания разговора.  |
| `muted` | Облако с меньшим акцентом для тихого вспомогательного контента.  |
| `tinted` | Тонкий пузырь основного оттенка.                        |
| `outline` | Пузырь с рамкой для второстепенного или богатого контента.       |
| `ghost` | Содержимое без рамки для текста помощника или расширенного контента.   |
| `destructive` | Разрушительный пузырь за ошибку или неудачные действия.      |

Размер пузырька соответствует его содержимому, до 80 % ширины контейнера. Вариант `ghost` удаляет максимальную ширину, поэтому вспомогательный текст и расширенный контент могут охватывать всю строку.

## Выравнивание

Используйте `align` на `bubble.root`, чтобы выровнять пузырь по началу или концу разговора.

**Использованный реквизит:** `align` на `bubble.root`.

```python
def bubble_alignment_demo():
    return rx.el.div(
        bubble.root(
            bubble.content(
                "This bubble is aligned to the start. This is the default alignment."
            ),
            variant="muted",
            align="start",
        ),
        bubble.root(
            bubble.content(
                "This bubble is aligned to the end. Use this for user messages."
            ),
            align="end",
        ),
        class_name="flex w-full max-w-sm flex-col gap-8 py-12",
    )
```

| выровнять | Описание |
| ------- | -------------------------------------------------- |
| `start` | Совместите пузырь с началом разговора. |
| `end` | Совместите пузырь с концом разговора.   |

**Примечание.** При создании интерфейсов чата вы, вероятно, захотите использовать выравнивание самого компонента `Message`, а не компонента `Bubble`. Вы можете использовать параметр `role` в компоненте `message.root`, чтобы автоматически выравнивать пузырь по началу или концу разговора.

## Пузырьковая группа

Используйте `bubble.group`, чтобы сгруппировать последовательные пузырьки от одного отправителя. Обратите внимание, что свойство `align` должно быть установлено в самом компоненте `bubble.root`, а не в компоненте `bubble.group`.

```composition
bubble.group
├── bubble.root
│   └── bubble.content
└── bubble.root
    └── bubble.content
```

**Использованный реквизит:** `align` на `bubble.root` (устанавливается для каждого пузыря, а не на `bubble.group`).

```python
def bubble_group_demo():
    return rx.el.div(
        bubble.root(
            bubble.content("Can you tell me what's the issue?"),
            variant="muted",
        ),
        bubble.group(
            bubble.root(
                bubble.content("You tell me!"),
                align="end",
            ),
            bubble.root(
                bubble.content("It worked yesterday. You broke it!"),
                align="end",
            ),
            bubble.root(
                bubble.content("Find the bug and fix it."),
                bubble.reactions(
                    rx.el.span("👀"),
                    aria_label="Reactions: eyes",
                    align="start",
                ),
                align="end",
            ),
        ),
        bubble.root(
            bubble.content(
                "Want me to diff yesterday's you against today's you? "
                "It's a bit embarrassing."
            ),
            variant="muted",
        ),
        class_name="flex w-full max-w-sm flex-col gap-8 py-12",
    )
```

## Ссылки и кнопки

Вы можете превратить пузырь в ссылку или кнопку, передав интерактивные элементы непосредственно в слот `bubble.content`. `bubble.content` принимает `*children`, поэтому простое размещение кнопки или ссылки отобразит этот компонент. 

**Использованный реквизит:** не требуется — передайте `button`/`rx.el.a` как дочерний элемент `bubble.content`.

```python
def bubble_link_button_demo():
    return rx.el.div(
        bubble.root(
            bubble.content("How can I help you today?"),
            variant="muted",
        ),
        bubble.group(
            bubble.root(
                bubble.content(
                    rx.el.button(
                        "I forgot my password",
                        on_click=rx.toast("You clicked forgot password"),
                        class_name="w-full text-left",
                    )
                ),
                variant="tinted",
                align="end",
            ),
            bubble.root(
                bubble.content(
                    rx.el.button(
                        "I need help with my subscription",
                        on_click=rx.toast("You clicked help with subscription"),
                        class_name="w-full text-left",
                    )
                ),
                variant="tinted",
                align="end",
            ),
            bubble.root(
                bubble.content(
                    rx.el.button(
                        "Something else. Talk to a human.",
                        on_click=rx.toast(
                            "You clicked something else. Talk to a human."
                        ),
                        class_name="w-full text-left",
                    )
                ),
                variant="tinted",
                align="end",
            ),
        ),
        class_name="flex w-full max-w-sm flex-col gap-8 py-12",
    )
```

## Реакции

Используйте `bubble.reactions` для реакций пузырьков. Вы можете использовать его для отображения реакций или кнопок быстрого действия. Используйте `side` и `align` для позиционирования строки — `side="top"` привязывает ее к верхнему краю. Реакции перекрывают край пузырька, поэтому оставляйте вертикальное пространство между строками — по этой причине в примерах ниже используется больший размер `gap`.

**Использованный реквизит:** `side`, `align` на `bubble.reactions`.

```python
def bubble_reactions_demo():
    return rx.el.div(
        bubble.root(
            bubble.content("I don't need tests, I know my code works."),
            bubble.reactions(
                rx.el.span("👍"),
                rx.el.span("😮"),
                align="start",
                role="img",
                aria_label="Reactions: thumbs up, surprised",
            ),
            variant="muted",
            align="end",
        ),
        bubble.root(
            bubble.content(
                "Bold. Fine I'll add some tests. I'll let you know when they're done."
            ),
            bubble.reactions(
                rx.el.span("👀"),
                rx.el.span("🚀"),
                rx.el.span("+2"),
                role="img",
                aria_label="Reactions: eyes, rocket, and 2 more",
            ),
            variant="muted",
        ),
        bubble.root(
            bubble.content(
                "Tests passed on the first try. All 142 of them. Looking good!"
            ),
            bubble.reactions(
                rx.el.span("🎉"),
                rx.el.span("👏"),
                side="top",
                align="start",
                role="img",
                aria_label="Reactions: party popper, clapping hands",
            ),
            variant="default",
            align="end",
        ),
        bubble.root(
            bubble.content("Are you sure I can run this command?"),
            bubble.reactions(
                rx.el.button(
                    "Yes, run it",
                    on_click=rx.toast.success("You clicked yes, running command..."),
                    class_name="px-2 py-0.5 text-xs hover:bg-accent rounded-md",
                ),
            ),
            variant="destructive",
        ),
        class_name="flex w-full max-w-sm flex-col gap-12 py-12",
    )
```

# Доступность

`bubble.root` отображает презентационную поверхность сообщения. Сохраняйте семантику уровня диалога в окружающем контейнере и следуйте приведенным ниже рекомендациям.

## Маркировка реакций

Реакции отображаются в виде ряда смайлов. Программа чтения с экрана считывает каждый глиф без контекста, а счетчики типа `+8` объявляются как «плюс восемь». Сгруппируйте строку как одно изображение с описательным `aria_label`, чтобы оно объявлялось один раз. `role="img"` также скрывает отдельные смайлы от вспомогательных технологий, поэтому `aria_hidden` не требуется.

```python
bubble.reactions(
    rx.el.span("👍"),
    rx.el.span("🔥"),
    rx.el.span("+8"),
    role="img",
    aria_label="Reactions: thumbs up, fire, and 8 more"
)
```

Если реакции интерактивны, вместо этого визуализируйте кнопки и присваивайте кнопкам, состоящим только из значков, `aria_label`.

```python
bubble.reactions(
    button(
        ...,
        aria_label="Thumbs up",
        variant="secondary",
        size="sm"
    )
)
```

## Интерактивные пузыри

Когда пузырь доступен для клика, визуализируйте его как настоящий `<button>` или `<a>`. Содержимое `bubble.-*` принимает `*children`, поэтому простая передача интерактивного компонента будет отображена. `bubble.content` содержит видимое кольцо фокусировки для интерактивных элементов, а доступное имя получается из текста в виде пузырька. Никакой дополнительной этикетки не требуется.

```python
bubble.root(
    bubble.content(
        "I forgot my password",
        rx.el.button(type="button", on_click=on_reply)
    ),
    variant="muted",
    align="end"
)
```

## Значение за пределами цвета

Варианты пузырьков сигнализируют о роли и тоне цвета. Соедините их с текстом, выравниванием или значками, чтобы смысл не передавался только цветом. Для пузырька `destructive` сохраняйте контекст ошибки в тексте сообщения, а не полагайтесь на цветовую обработку.

# Справочник по API

## пузырь.корень

Корневая пузырчатая обертка.

| Опора | Тип | По умолчанию | Описание |
| ----------- | ------------------------------------------------------------------------------------------ | ----------- | ------------------------------------------------ |
| `variant` | `Literal["default", "secondary", "muted", "tinted", "outline", "ghost", "destructive"]` | `"default"` | Визуальная обработка пузырьков.                     |
| `align` | `Literal["start", "end"]` | `"start"` | Линейное выравнивание пузырька.              |
| `class_name` | `str` | - | Дополнительные классы, применяемые к корневому элементу. |

## пузырь.контент

Обертка пузырькового содержимого.

| Опора | Тип | По умолчанию | Описание |
| ----------- | -------------------------- | ------- | ----------------------------------------- |
| `*children` | `*rx.Component` | - | Отобразите контент как отдельный элемент, например ссылку. |
| `class_name` | `str` | - | Дополнительные классы, которые можно применить к элементу контента.       |

## пузырь.реакции

Отображает перекрывающиеся реакции на пузырь.

| Опора | Тип | По умолчанию | Описание |
| ----------- | ------------------- | ---------- | ------------------------------------------------ |
| `side` | `Literal["top", "bottom"]` | `"bottom"` | Сторона пузыря, на которой закрепляются реакции.  |
| `align` | `Literal["start", "end"]` | `"end"` | Встроенное выравнивание реакций.           |
| `class_name` | `str` | - | Дополнительные классы, которые можно применить к строке реакции. |

## пузырь.группа

Группирует последовательные пузырьки от одного отправителя.

| Опора | Тип | По умолчанию | Описание |
| ----------- | -------- | ------- | --------------------------------------------- |
| `class_name` | `str` | - | Дополнительные классы для применения к корню группы. |